前言:
这是一篇关于JavaScript的学习笔记,目的是针对java后端开发人员,快速入门并掌握JavaScript这门语言的基本使用,并且能够进入下一阶段框架的学习。
引言:
为什么学习 JavaScript?
JavaScript 是 web 开发人员必须学习的 3 门语言中的一门:
-
HTML 定义了网页的内容(网页的结构)
-
CSS 描述了网页的布局(网页的表现)
-
JavaScript 控制了网页的行为(交互效果)
简单来说:我们使用HTML和CSS编写的网页都是静态的,现在我们想要让网页可以进行交互,变成动态的网页,就需要JavaScript来完成
JavaScript 简介
JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。
扩展:了解即可
JavaScript 已经由 ECMA(欧洲电脑制造商协会)通过 ECMAScript 实现语言的标准化。
年份 名称 描述 1997 ECMAScript 1 第一个版本 1998 ECMAScript 2 版本变更 1999 ECMAScript 3 添加正则表达式 添加 try/catch ECMAScript 4 没有发布 2009 ECMAScript 5 添加 "strict mode",严格模式 添加 jsON 支持 2011 ECMAScript 5.1 版本变更 2015 ECMAScript 6 添加类和模块 2016 ECMAScript 7 增加指数运算符 (**) 增加 Array.prototype.includes ECMAScript 6 也称为 ECMAScript 2015。
ECMAScript 7 也称为 ECMAScript 2016。
以上可以理解为各个版本的JavaScript,我们现在使用的JS指的就是ECMAScript6 -7的标准
JavaScript 是脚本语言
JavaScript 是一种轻量级的编程语言。
JavaScript 是可插入 HTML 页面的编程代码。
JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行。
JS学习
js(JavaScript)的引入方式
-
内部脚本:将js代码定义在HTML页面中
-
JavaScript代码必须位于
<script>
</script>
标签之间 -
在HTML文档中,可以在任意地方,放置任意数量的
<script>
-
一般会把脚本置于
<body>
元素的底部,可改善显示速度
<script> alert("Hello JavaScript"); </script>
-
-
外部脚本:将js代码定义在外部js文件中,然后引入到HTML页面中
-
外部js文件中,只包含js代码,不包含
<script>
标签 -
<script>
标签不能自闭合(只能使用双标签)
<script src="js/demo.js"></script> alert("Hello JavaScript");
-
注意:
<script>
标签可以放在网页的任意位置,但是由于加载顺序的原因,显示也会受到影响,但是后期如果要引入外部的js库的情况下,就需要放在body上方(需要外部js的支持,只能先加载)
小案例:修改页面内容
HTML:
<body>
<p id="p1">Hello World!</p>
</body>
<script>
let e =document.getElementById('p1')
e.innerText="Hello JavaScript";
</script>
扩展:常见的输出语句
<script> window.alert("Hello JavaScript");//浏览器弹出警告框 document.write("Hello JavaScript");//写入HTML,在浏览器展示 console.log("Hello JavaScript");//写入浏览器控制台 </script>
js基础语法
书写语法
-
区分大小写:与Java一样,变量名、函数名以及其他一切都区分大小写的
-
每行结尾的分号可有可无
-
注释:
-
单行注释:
//注释内容
-
多行注释:
/*注释内容*/
-
-
大括号代表代码块
js不属于强类型语言,后面的分号可有可无,但是推荐加上,因为后面一些框架可能会对js进行优化(去掉所有空格),此时就需要分号作为语法分隔符
变量
-
JavaScript使用
var
关键字(variable的缩写)来声明变量。 -
JavaScript是一门弱类型语言,变量可以存放不同类型的值
-
变量需要遵循如下规则:
-
组成字符可以是任何字母、数字、下划线(_)或美元符号($)
-
数字不能开头
-
建议使用驼峰命名
-
ECMAScript6
新增了let
关键字来定义变量,用法类似var
,但声明的变量,只能在let
关键字所在的代码块内有效、且不允许重复声明(可以重复赋值,但不能重复定义)
可以理解为let为java中的局部变量
ECMAScript
新增了const
关键字,用来声明一个只读的常量,一旦声明,常量的值就不能改变
数据类型、运算符、流程控制语句
数据类型
-
JavaScript中分为:原始类型和引用类型
-
原始类型(基本类型):
-
number:数字(整数、小数、NaN(Not a Number))
-
string:字符串、单双引皆可
-
boolean:布尔。true,false
-
null:对象为空
-
undefined:当声明的变量未初始化时,该变量的默认值是undefined
-
-
使用
typeof
运算符可以获取数据类型:-
<script> var a=20; alert(type0f(a)); </script>
-
基本类型
undefined和null
-
执行表达式或函数,没有返回结果,出现 undefined
-
访问数组不存在的元素,访问对象不存在的属性,出现 undefined
-
定义变量,没有初始化,出现 undefined
例:
console.log(1); // 函数没有返回值, 结果是 undefined
let a = 10; // 表达式没有返回值, 结果是 undefined
let b = [1,2,3];
console.log(b[10]); // 数组未定义元素是 undefined
let c = {"name":"张三"};
console.log(c.age); // 对象未定义属性是 undefined
let d;
console.log(d); // 变量未初始化是 undefined
undefined和null的区别和联系
二者共同点
-
都没有属性、方法 (基本类型都没有,有例外:string)
-
二者合称 Nullish(只是一个称呼)
二者区别
-
undefined 由 js产生
-
null 由程序员提供
string
js 字符串三种写法
let a = "hello"; // 双引号
let b = 'world'; // 单引号
let c = `hello`; // 反引号
js的字符串拼接
需求:拼接 URI 的请求参数,如
传统方法拼接:
let name = ; // zhang li ...
let age = ; // 18 20 ...
let url = "/test?name=" + name + "&age=" + age;
模板字符串方式:(模版字符串只能使用反引号)
let name = ; // zhang li ...
let age = ; // 18 20 ...
let url = `/test?name=${name}&age=${age}`;
使用模版字符串是为了提高可读性
number和bigint
number 类型标识的是双精度浮动小数,例如
10 / 3; // 结果 3.3333333333333335
既然是浮点小数,那么可以除零
10 / 0; // 结果 Infinity 正无穷大
-10 / 0; // 结果 -Infinity 负无穷大
浮点小数都有运算精度问题,例如
2.0 - 1.1; // 结果 0.8999999999999999
bigint
可以用在一个整数字面量后面加 n
的方式定义一个 BigInt
,如:10n
alert(10n/3n) //结果为3
boolean
-
Truthy
-
Falsy
在 js 中,并不是 boolean 才能用于条件判断,你可以在 if 语句中使用【数字】、【字符串】... 作为判断条件
let b = 1;
if(b) { // true
console.log("进入了");
}
这时就有一个规则,当需要条件判断时,这个值被当作 true 还是 false,当作 true 的值归类为 truthy,当作 false 的值归类为 falsy
下面值都是 falsy
-
false
-
Nullish (null, undefined)
-
0, 0n, NaN
-
"" '' ``
即长度为零的字符串
剩余的值绝大部分都是 truthy
有几个容易被当作 falsy 实际是 truthy 的
-
"false", "0"
即字符串的 false 和 字符串的零(本质上还是有值的字符串,为true) -
[]
空数组 -
{}
空对象
symbol
很少使用
symbol是一个ES6标准种新增的一种基本数据类型,在JavaScript中,共有七种基本数据类型:string、number、bigint、boolean、null、undefined、symbol。并且除了null和undefined之外,每个基本类型都有其包装对象。
symbol 的值是通过 Symbol() 函数生成,每一个 symbol 的值都是唯一的,并且 symbol 类型的值可以作为对象的属性标识符使用,这也是 symbol 类型设计的目的。
类型转换
-
字符串类型转为数字
-
将字符串字面值转为数字,如果字面值不是数字,则转为NaN
-
-
其他类型转为boolean
-
number:0和NaN为false,其他均转为true
-
string:空字符串为false,其他均转为true
-
null和undefined:均转为false
-
字符串转数字
parseInt("10"); // 结果是数字 10
parseInt("10.5"); // 结果是数字 10, 去除了小数部分
parseInt("10") / 3; // 结果仍视为 number 浮点数, 因此结果为 3.3333333333333335
parseInt("abc"); // 转换失败,结果是特殊值 NaN (Not a Number)
也可以使用字符串减去零的方式完成字符串的转换成数字,因为在减法运算时,字符串已经转换成数字类型了
引用类型(对象类型):
1.Function
定义函数
方式一:
function 函数名(参数) {
// 函数体
return 结果;
}
例
function add(a, b) {
return a + b;
}
方式二:
var 函数名 = function(参数) {
// 函数体
return 结果;
}
例
var add = function(a, b) {
return a + b;
}
函数:是被设计用来执行特定任务的代码块
因为js的是弱类型的语言,所以使用函数时要注意:
形式参数不需要类型
返回值也不需要定义类型,可以在函数内部直接使用return返回即可(如果没有返回值,不写return语句即可)
调用函数
例
add(1, 2); // 返回 3
js 中的函数调用特点:对参数的类型和个数都没有限制,例如
add('a', 'b'); // 返回 ab
add(4, 5, 6); // 返回 9, 第三个参数没有被用到, 不会报错
add(1); // 返回 NaN, 这时 b 没有定义是 undefined, undefined 做数学运算结果就是 NaN
js调用函数时,可以传递任意个数参数,只不过,超出定义部分的参数不会被接收
默认参数
js(如果调用方法时,没有传入参数,则使用默认参数)
function pagination(page = 1, size = 10) {
console.log(page, size);
}
匿名函数
语法(建议将匿名函数用小括号包括起来,表示该函数是一个表达式)
(function (参数) {
// 函数体
return 结果;
})
例
(function(a,b){
return a + b;
})
第一种场景:定义完毕后立刻调用,只调用一次
(function(a,b){
return a + b;
})(1,2)
第二种场景:作为其它对象的方法,例如
页面有元素
<p id="p1">点我啊</p>
此元素有一个 onclick 方法,会在鼠标单击这个元素后被执行,onclick 方法刚开始的值是 null,需要赋值后才能使用(函数可以被用来赋值,因为在js中函数也被认定为一个对象)
document.getElementById("p1").onclick = (function(){
console.log("鼠标单击了...");
});
箭头函数
(可以理解为匿名函数的简化写法)
(参数) => {
// 函数体
return 结果;
}
-
如果没有参数,() 还是要保留
-
如果只有一个参数,() 可以省略
-
如果函数体内只有一行代码,{} 可以省略
-
如果这一行代码就是结果,return 可以省略
例
document.getElementById("p1").onclick = () => console.log("aa");
函数是对象
//在Javascript中,函数本身也是对象
以下形式在 js 中非常常见!
- 可以参与赋值,具名函数(有具体名字的函数)也能参与赋值
function abc() {
console.log("bb");
}
document.getElementById("p1").onclick = abc; //函数本身作为值进行传递,不是调用函数获取返回值
- 函数也是对象,对象有属性、有方法,执行
console.dir(abc)
,输出结果如下 -
ƒ abc() arguments: null caller: null length: 0 name: "abc" ➡prototype: {constructor: ƒ} [[FunctionLocation]]: VM1962:1 ➡[[Prototype]]: ƒ () ➡[[Scopes]]: Scopes[1]
-
其中带有 f 标记的是方法,不带的是属性
-
带有 ➡ 符号的可以继续展开,限于篇幅省略了
-
带有
[[ ]]
的是内置属性,不能访问,只能查看 -
相对重要的是
[[Prototype]]
和[[Scopes]]
会在后面继承和作用域时讲到
-
- 可以作为方法参数 (这里的b成为高阶函数)
function a() {
console.log('a')
}
function b(fn) { // fn 将来可以是一个函数对象
console.log('b')
fn(); // 调用函数对象
}
b(a)
- 可以作为方法返回值(这里的c也可以叫做高阶函数)
function c() {
console.log("c");
function d() {
console.log("d");
}
return d;
}
c()() //返回值是一个函数, 再次进行函数调用 (两次函数调用)
函数作用域
函数可以嵌套(js 代码中很常见,只是嵌套的形式更多是匿名函数,箭头函数)
function a() {
function b() {
}
}
看下面的例子
function c() {
var z = 30;
}
var x = 10;
function a() {
var y = 20;
function b() {
// 看这里
console.log(x, y);
}
b();
}
a();
对于嵌套的函数b,他的作用域从自身到外部函数,到全局作用域一共三层
-
以函数大括号为分界线划定作用域,所有函数之外是全局作用域
-
查找变量时,由内向外查找
-
在内层作用域找到变量,就会停止查找,不会再找外层
-
所有作用域都找不到变量,报错
-
-
作用域本质上是函数对象的属性,可以通过 console.dir 来查看调试(就是scopes属性)
闭包
var x = 10;
function a() {
var y = 20;
function b() {
console.log(x,y);
}
return b;
}
a()(); // 在外面执行了 b
-
函数定义时,它的作用域已经确定好了,因此无论函数将来去了哪,都能从它的作用域中找到当时那些变量(因为作用域本质上是函数的一个属性)
-
别被概念忽悠了,闭包就是指函数能够访问自己的作用域中变量
let、var 与作用域
如果函数外层引用的是 let 变量,那么外层普通的 {} 也会视为作用域边界,最外层的 let 也占一个 script 作用域
let x = 10;
if(true) {
let y = 20;
function b() {
console.log(x,y);
}
console.dir(b);
}
如果函数外层引用的是 var 变量,外层普通的 {} 不会视为边界
var x = 10;
if(true) {
var y = 20;
function b() {
console.log(x,y);
}
console.dir(b);
}
如果 var 变量出现了重名,则他俩会被视为同一作用域中的同一个变量
var e = 10;
if(true) {
var e = 20;
console.log(e); // 打印 20
}
console.log(e); // 因为是同一个变量,还是打印 20
如果是 let,则视为两个作用域中的两个变量
let e = 10;
if(true) {
let e = 20;
console.log(e); // 打印 20
}
console.log(e); // 打印 10
要想里面的 e 和外面的 e 能区分开来,最简单的办法是改成 let,或者用函数来界定作用域范围(只要保证变量不在同一个作用域,则不是同一个变量)
var e = 10;
if(true) {
function b() {
var e = 20;
console.log(e);
}
b();
}
console.log(e);
简而言之:可以理解为
let
定义的变量为局部的,var
定义的变量为全局的
2.JS对象
主要学习三大类对象:基础对象,浏览器对象,文档对象
基础对象是js中自带的一些对象
BOM对象(将浏览器中的组件封装成对象)
DOM对象(将html中的标签封装成对象)
Array
-
JavaScriprt中Array对象用于定义数组
-
定义
-
var 变量名 =new Array(元素列表); //方式一 var arr =new Array(1,2,3,4);
-
var 变量名 =[元素列表];//方式二 var arr=[1,2,3,4];
-
-
访问
-
arr[索引]=值; arr[10]="hello";
-
-
js中的数组类似java中的集合,长度可变,类型可变。
例:
// 创建数组
let arr = [1,2,3];
// 获取数组元素
console.log(arr[0]); // 输出 1
// 修改数组元素
array[0] = 5; // 数组元素变成了 [5,2,3]
// 遍历数组元素,其中 length 是数组属性,代表数组长度
for(let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
常用API
-
push、shift、splice
let arr = [1,2,3];
arr.push(4); // 向数组尾部(右侧)添加元素, 结果 [1,2,3,4]
arr.shift(); // 从数组头部(左侧)移除元素, 结果 [2,3,4]
arr.splice(1,1); // 删除【参数1】索引位置的【参数2】个元素,结果 [2,4]
-
join(字符串连接)
let arr = ['a','b','c'];
arr.join(); // 默认使用【,】作为连接符,结果 'a,b,c'
arr.join(''); // 结果 'abc'
arr.join('-'); // 结果 'a-b-c'
-
map、filter、forEach
let arr = [1,2,3,6];
function a(i) { // 代表的新旧元素之间的变换规则
return i * 10
}
// arr.map(a) // 具名函数,结果 [10,20,30,60]
// arr.map( (i) => {return i * 10} ); // 箭头函数
arr.map( i => i * 10 ); // 箭头函数
-
传给 map 的函数,参数代表旧元素,返回值代表新元素
map 的内部实现(伪代码)
function map(a) { // 参数是一个函数
let narr = [];
for(let i = 0; i < arr.length; i++) {
let o = arr[i]; // 旧元素
let n = a(o); // 新元素
narr.push(n);
}
return narr;
}
filter 例子
let arr = [1,2,3,6];
arr.filter( (i)=> i % 2 == 1 ); // 结果 [1,3]
-
传给 filter 的函数,参数代表旧元素,返回 true 表示要留下的元素
forEach 例子
let arr = [1,2,3,6];
/*for(let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}*/
arr.forEach( (i) => console.log(i) );
两个称呼
-
高阶函数,map,filter,forEach
-
回调函数,例如作为参数传入的函数
String
-
字符串:既像基本类型
string
,又像对象object
-
定义
-
var 变量名 =new String("..."); //方式一 var str =new String("Hello String");
-
var 变量名 ="...";//方式二 var str="Hello String";
-
-
属性:length 描述:字符串的长度
-
方法
-
charAt() 描述:返回指定位置的字符
-
indexOf() 描述:检索字符串
-
trim() 描述:去除字符串两边的空格
-
substring() 描述:提取字符串中两个指定的索引号之间的字符
-
Object
JavaScript自定义对象
-
定义格式
-
var 对象名={ 属性名1:属性值1, 属性名2:属性值2, 属性名3:属性值3, 函数名称:function(){} }; var user={ name="Tom", age:20, gender:"male", eat:function(){ alert("吃饭..."); } }; //函数的省略形式 var user={ name="Tom", age:20, gender:"male", eat(){ alert("吃饭..."); } };
-
-
调用格式
-
//调用格式 对象名.属性名; 对象名.函数名();
-
Object语法
get和set方法
let obj = {
属性名: 值,
方法名: 函数,
get 属性名() {},
set 属性名(新值) {}
}
创建一个对象的多种方式如下:
例1
let stu1 = {
name: "小明",
age: 18,
study: function(){
console.log(this.name + "爱学习");
}
}
例2
let name = "小黑";
let age = 20;
let study = function(){
console.log(this.name + "爱学习");
}
let stu2 = { name, age, study } //解构运算符进行对象创建,运算符中会讲到,现在可以理解为将变量直接作为对象的属性进行创建对象
例3(重点)
let stu3 = {
name: "小白",
age: 18,
study(){
console.log(this.name + "爱学习");//对象方法这么写,仅限于对象内部
}
}
例4:(书写对象中的get和set方法)
let stu4 = {
_name: null, /*类似于java中私有成员变量*/
get name() {
console.log("进入了get");
return this._name;
},
set name(name) {
console.log("进入了set");
this._name = name;
}
}
调用 get,set(不是直接调用方法,而是对象名.属性名
赋值时默认调用set,对象名.属性名=值
取值时默认调用get)
stu4.name = "小白"
console.log(stu4.name)
js中并没有私有属性的概念,加上下划线只是一种约定俗成的规则,我们还是可以直接访问该属性的,但是一般不这样做
特色:属性增删
对比一下 Java 中的 Object
-
Java 的 Object 是以类作为模板来创建,对象不能脱离类模板的范围,一个对象的属性、能用的方法都是在编译时确定好的
-
js 的对象,不需要什么模板,它的属性和方法可以随时增加或删减
let stu = {name:'张三'};
stu.age = 18; // 添加属性
delete stu.age; // 删除属性
stu.study = function() { // 添加方法
console.log(this.name + "在学习");
}
添加 get,set,需要借助 Object.definePropery方法
let stu = {_name:null};
Object.defineProperty(stu, "name", {
get(){
return this._name;
},
set(name){
this._name = name;
}
});
-
参数1:目标对象
-
参数2:属性名
-
参数3:get,set 的定义
特色:this
先来对 Java 中的 this 有个理解
public class TestMethod {
static class Student {
private String name;
public Student(String name) {
this.name = name;
}
public void study(Student this, String subject) {
System.out.println(this.name + "在学习 " + subject);
}
}
public static void main(String[] args) {
Student stu = new Student("小明");
// 下面的代码,本质上是执行 study(stu, "java"),因此 this 就是 stu
stu.study("java");
}
}
-
Java 中的 this 是个隐式参数
-
Java 中,我们说 this 代表的就是调用方法的那个对象
js 中的 this 也是隐式参数,但它与函数运行时上下文相关
例如:
1.一个“落单”的函数
function study(subject) {
console.log(this.name + "在学习 " + subject)
}
测试一下
study("js"); // 输出 在学习 js
没有获取到输入的值,这是因为在函数执行时,全局对象 window 被当作了 this,window 对象的 name 属性是空串,所以不显示
2.同样的函数,如果作为对象的方法
let stu = {
name:"小白",
study
}
这种情况下,会将当前对象作为 this
stu.study('js'); // 输出 小白在学习 js
还可以动态改变 this
let stu = {name:"小黑"};
study.call(stu, "js"); // 输出 小黑在学习 js
这回 study 执行时,就把 call 的第一个参数 stu 作为 this
3.一个例外是,在箭头函数内出现的 this,以外层 this 理解
用匿名函数
let stu = {
name: "小花",
friends: ["小白","小黑","小明"],
play() {
this.friends.forEach(function(e){
console.log(this.name + "与" + e + "在玩耍");
});
}
}
stu.play()
-
this.name 所在的函数是“落单”的函数,因此 this 代表 window对象
输出结果为
与小白在玩耍
与小黑在玩耍
与小明在玩耍
用箭头函数
let stu = {
name: "小花",
friends: ["小白","小黑","小明"],
play() {
this.friends.forEach(e => {
console.log(this.name + "与" + e + "在玩耍");
})
}
}
-
this.name 所在的函数是箭头函数,因此 this 要看它外层的 play 函数,play 又是属于 stu 的方法,因此 this 代表 stu 对象
输出结果为
小花与小白在玩耍
小花与小黑在玩耍
小花与小明在玩耍
不用箭头函数的做法
let stu = {
name: "小花",
friends: ["小白","小黑","小明"],
play() {
let me = this;
this.friends.forEach(function(e){
console.log(me.name + "与" + e + "在玩耍");
});
}
}
特色:原型继承
(js中指的是继承发生在两个对象之间,而不是两个类之间)
let father = {
f1: '父属性',
m1: function() {
console.log("父方法");
}
}
let son = Object.create(father); //调用方法,以father对象为原型创建son对象
console.log(son.f1); // 打印 父属性
son.m1(); // 打印 父方法
-
father 是父对象,son 去调用 .m1 或 .f1 时,自身对象没有,就到父对象找
-
son 自己可以添加自己的属性和方法
-
son 里有特殊属性
__proto__
代表它的父对象,js 术语: son 的原型对象 -
不同浏览器对打印 son 的
__proto__
属性时显示不同 -
-
Edge 打印 console.dir(son) 显示
[[Prototype]]
-
Firefox 打印 console.dir(son) 显示
<prototype>
-
特色:基于函数的原型继承
出于方便的原因,js 又提供了一种基于函数的原型继承
函数职责
-
负责创建子对象,给子对象提供属性、方法,功能上相当于构造方法
-
函数有个特殊的属性 prototype,它就是函数创建的子对象的父对象 注意!名字有差异,这个属性的作用就是为新对象提供原型
function cons(f2) {
// 创建子对象(this), 给子对象提供属性和方法
this.f2 = f2;
this.m2 = function () {
console.log("子方法");
}
}
// cons.prototype 就是父对象
cons.prototype.f1 = "父属性";
cons.prototype.m1 = function() {
console.log("父方法");
}
配合 new 关键字,创建子对象
let son = new cons("子属性")
子对象的 __proto__
就是函数的 prototype
属性
JSON
-
概念:JavaScript Object Notation ,JavaScript对象标记法
-
JSON是通过JavaScript对象标记法书写的文本
-
由于其语法简单,层次结构鲜明,多用于作为数据载体,在网络中进行数据传输
可以理解为,JSON本质上就是一个字符串,作用是为了作为数据的载体在网络上进行传输
json常用于作为数据的载体,因为xml格式作为数据传输载体过于臃肿,可以用json代替,json与自定义js对象格式相似,用来承载复杂数据,不过json中所有的键必须用双引号标注。
JSON基础语法
var 变量名='{"key1":value1,"key2":value2,...}';
var userStr='{"name":"Jerry","age":18,"addr":["北京","上海","河南"]}';
value的数据类型为:
数字(整数或浮点数)
字符串(在双引号中)
逻辑值(true或false)
数组(在方括号中)
对象(在花括号中)
null
使用
-
前端向后端传递复杂数据时,可以使用json
-
将js对象转换成json格式数据,function函数不会保留
-
json本身只是一个字符串不能直接使用对象调用属性和方法的形式,必须先转化
JSON字符串转为JS对象
var jsObject=JSON.parse(userStr);
JS对象转为JSON字符串
var jsonStr =JSON.stringify(jsObject);
了解更多:
js对象和JSON字符串的区别详解:
一个 json 对象可以长这样:
{ "name":"VaporGas", "age":22 }
一个 js 对象可以长这样:
{ name:"VaporGas", age:22 }
那么他们的区别在哪儿呢?我总结了这么几点
本质不同
json 对象本质上是个字符串,它的职责是作为客户端和服务器之间传递数据的一种格式,它的属性只是样子货
js 对象是切切实实的对象,可以有属性方法
语法细节不同
json 中只能有 null、true|false、数字、字符串(只有双引号)、对象、数
json 中不能有除以上的其它 js 对象的特性,如方法等
json 中的属性必须用双引号引起来
json 字符串与 js 对象的转换
JSON.parse(json字符串); // 返回js对象 JSON.stringify(js对象); // 返回json字符串
动态类型
java是强类型的语言(变量和值都有类型),js是弱类型的语言(值有类型,变量没有类型)
静态类型语言,如 Java,值有类型,变量也有类型、赋值给变量时,类型要相符
int a = 10;
String b = "abc";
int c = "abc"; // 错误
而 js 属于动态类型语言,值有类型,但变量没有类型,赋值给变量时,没要求
例如
let a = 200;
let b = 100;
b = 'abc';
b = true;
动态类型看起来比较灵活,但变量没有类型,会给后期维护带来困难,例如
function test(obj) {
// obj 的类型未知,必须根据不同类型做出相应的容错处理
}
运算符
算术运算符:
+
,-
,*
,/
,%
,++
,--
赋值运算符:
=
,+=
,-=
,*=
,/=
,%=
比较运算符:
>
,<
,<=
,>=
,!=
,==,
===
逻辑运算符:
&&
,||
,!
三元运算符:
条件表达式?true_value:false_value
==
与===
的区别
-
==
会进行类型转换 ,===
不会进行类型转换
var a = 10;
alert(a =="10");//true
alert(a ==="10");//false
alert(a === 10); //true
比较重要的一些运算符:
===
严格相等运算符,用作逻辑判等
1 == 1 // 返回 true
1 == '1' // 返回 true,会先将右侧的字符串转为数字,再做比较
1 === '1' // 返回 false,类型不等,直接返回 false
typeof 查看某个值的类型
typeof 1 // 返回 'number'
typeof '1' // 返回 'string'
||
需求,如果参数 n 没有传递,给它一个【男】
推荐做法
function test(n = '男') {//是一种赋默认值的方式,如果有值,会替代默认值
console.log(n);
}
你可能的做法
function test(n) {
if(n === undefined) {
n = '男';
}
console.log(n);
}
还可能是这样
function test(n) {
n = (n === undefined) ? '男' : n; //三元运算符
console.log(n);
}
以上都是一些老旧代码中可能的做法(不推荐)
使用||
运算符:
function test(n) {
n = n || '男';
console.log(n);
}
它的语法是
值1 || 值2
如果值1 是 Truthy,返回值1,如果值1 是 Falsy 返回值 2
??
与?
??
需求,如果参数 n 没有传递或是 null,给它一个【男】
如果用传统办法
function test(n) {
if(n === undefined || n === null) {
n = '男';
}
console.log(n);
}
用 ??
function test(n) {
n = n ?? '男';
console.log(n);
}
语法
值1 ?? 值2
-
值1 是 nullish,返回值2
-
值1 不是 nullish,返回值1
?.
需求,函数参数是一个对象,可能包含有子属性
例如,参数可能是
let stu1 = {
name:"张三",
address: {
city: '北京'
}
};
let stu2 = {
name:"李四"
}
let stu3 = {
name:"李四",
address: null
}
现在要访问子属性(出现异常,因为address对象不存在city属性)
function test(stu) {
console.log(stu.address.city)
}
现在希望当某个属性是 nullish 时,短路并返回 undefined,可以用?.
function test(stu) {
console.log(stu.address?.city)
}
用传统办法
function test(stu) {
if(stu.address === undefined || stu.address === null) {
console.log(undefined);
return;
}
console.log(stu.address.city)
}
...
展开运算符
作用1:打散数组,把元素传递给多个参数
let arr = [1,2,3];
function test(a,b,c) {
console.log(a,b,c);
}
需求,把数组元素依次传递给函数参数
传统写法
test(arr[0],arr[1],arr[2]); // 输出 1,2,3
展开运算符写法
test(...arr); // 输出 1,2,3
-
打散可以理解为【去掉了】数组外侧的中括号,只剩下数组元素
作用2:复制数组或对象
数组
let arr1 = [1,2,3];
let arr2 = [...arr1]; // 复制数组
对象
let obj1 = {name:'张三', age: 18};
let obj2 = {...obj1}; // 复制对象
注意:展开运算符复制属于浅拷贝,例如(如果只有一层,则被拷贝过去的改变不会影响被拷贝对象,如果有多层,拷贝的二层及以下就是深拷贝,改动会造原拷贝改变)
let o1 = {name:'张三', address: {city: '北京'} }
let o2 = {...o1};
作用3:合并数组或对象
合并数组
let a1 = [1,2];
let a2 = [3,4];
let b1 = [...a1,...a2]; // 结果 [1,2,3,4]
let b2 = [...a2,5,...a1] // 结果 [3,4,5,1,2]
合并对象
let o1 = {name:'张三'};
let o2 = {age:18};
let o3 = {name:'李四'};
let n1 = {...o1, ...o2}; // 结果 {name:'张三',age:18}
let n2 = {...o3, ...o2, ...o1}; // 结果{name:'李四',age:18}
-
复制对象时出现同名属性,后面的会覆盖前面的
[]
{}
解构运算符
[]
用在声明变量时
let arr = [1,2,3];
let [a, b, c] = arr; // 结果 a=1, b=2, c=3
用在声明参数时
let arr = [1,2,3];
function test([a,b,c]) {
console.log(a,b,c) // 结果 a=1, b=2, c=3
}
test(arr);
{}
用在声明变量时
let obj = {name:"张三", age:18};
let {name,age} = obj; // 结果 name=张三, age=18
用在声明参数时 (注意需要变量名称与对象的属性名一致)
let obj = {name:"张三", age:18};
function test({name, age}) {
console.log(name, age); // 结果 name=张三, age=18
}
test(obj)
控制语句
-
if ... else
-
switch
-
while
-
do ... while
-
for
-
for ... in
-
for ... of
-
try ... catch
本文只介绍较难理解的的后三个控制语句
for in
主要用来遍历对象
let father = {name:'张三', age:18, study:function(){}};
for(const n in father) {
console.log(n);
}
-
其中 const n 代表遍历出来的属性名(为什么使用const,是因为for in 有严格的迭代过程,每次遍历都会取出一个新的const,每个const都是不可变的)
-
注意1:方法名也能被遍历出来(它其实也算一种特殊属性)
-
注意2:遍历子对象时,父对象的属性会跟着遍历出来
let son = Object.create(father);
son.sex = "男";
for(const n in son) {
console.log(n);
}
-
注意3:在 for in 内获取属性值,要使用 [] 语法,而不能用 . 语法
for(const n in son) {
console.log(n, son[n]);//n是属性名,son[n]是对应的属性值
}
for of
主要用来遍历数组,也可以是其它可迭代对象,如 Map,Set 等
let a1 = [1,2,3];
for(const i of a1) {
console.log(i);
}
let a2 = [
{name:'张三', age:18},
{name:'李四', age:20},
{name:'王五', age:22}
];
for(const obj of a2) {
console.log(obj.name, obj.age);
}
for(const {name,age} of a2) {
console.log(name, age);
}
try catch
let stu1 = {name:'张三', age:18, address: {city:'北京'}};
let stu2 = {name:'张三', age:18};
function test(stu) {
try {
console.log(stu.address.city)
} catch(e) {
console.log('出现了异常', e.message)
} finally {
console.log('finally');
}
}
BOM
-
概念:Browser Object Model 浏览器对象模型,允许JavaScript与浏览器直接对话,JavaScript将浏览器的各个组成部分封装为对象。
-
组成:
-
Window:浏览器窗口对象
-
Navigator:浏览器对象
-
Screen:屏幕对象
-
History:历史记录对象
-
Location:地址栏对象
-
Window
-
介绍:浏览器窗口对象。
-
获取:直接使用window,其中 window.可以省略。
-
window.alert("Hello window");
-
alert("Hello window");
-
-
属性
-
history:对 History 对象的只读引用。
-
location:用于窗口或框架的 Location 对象。
-
navigator:对 Navigator 对象的只读引用。
-
-
方法
-
alert():显示带有一段消息和一个确认按钮的警告框。
-
confirm():显示带有一段消息以及确认按钮和取消按钮的对话框。
-
setlnterval():按照指定的周期(以毫秒计)来调用函数或计算表达式。
-
setTimeout():在指定的毫秒数后调用函数或计算表达式。
-
Location
-
介绍:地址栏对象
-
获取:使用
window.location
获取,其中window.
可以省略 -
属性
-
href:设置或返回完整的URL
-
location.href="https://www.baidu.com"
-
以上代码先显示当前地址栏的地址信息,然后,跳转到指定的网址。
DOM
HTML文档被浏览器加载并解析后,会封装成对应的对象,同时在浏览器的内存中形成DOM树
-
概念:Document Object Model,文档对象模型
-
将标记语言的各个组成部分封装为对应的对象:
-
Document:整个文档对象
-
Element:元素对象
-
Attribute:属性对象
-
Text:文本对象
-
Comment:注释对象
-
js通过DOM和事件监听,控制网页的行为。
-
JavaScript通过DOM,就能够对HTML进行操作:
-
改变HTML元素的内容
-
改变HTML元素的样式(CSS)
-
对HTML DOM事件作出反应
-
添加和删除HTML元素
-
W3C DOM
由以下三部分组成:
核心DOM
- 针对任何结构化文档的标准模型。
Document:整个文档对象
Element:元素对象
Attribute:属性对象
Text:文本对象
Comment:注释对象
XML DOM
- 针对XML
文档的标准模型。
HTML DOM
- 针对HTML
文档的标准模型。
DOM获取元素对象
获取对应的元素对象之后,就能对元素对象进行相关的操作了
事件监听
事件:就是发生在HTML元素上的动作
事件监听:就是当动作发生时,执行相应的操作
事件绑定:
注意:在属性上绑定事件,是方法的调用,因此需要加()表示调用此方法,如果需要传入参数则写入准确的参数。此方法可以传参数;
而通过dom元素属性进行绑定一般不传递参数,不写()是因为把函数作为对象(参数)传入
常见的事件
标签:function,Java,name,对象,JavaScript,程序员,let,console,log From: https://blog.csdn.net/weixin_52937170/article/details/141688070总结,以上是我学习js的一点笔记总结,推荐去一些教学网站(例如菜鸟教程)进一步学习
较为深刻的理解一下:JavaScript是一个基于客户端浏览器,基于对象和事件驱动式的网页脚本语言