1. 严格模式
1.1 严格模式的使用方法
使用方法1:"use strict"; 开启严格模式。
使用方法2:<script type="moaule"></script> 当设置script标签为模块化时,自动启用严格模式。
1.2 严格模式的限制
1. 要求变量不能重名。
//报错
"use strict";
var a=2;
var a=4;
2. 函数中的参数不能同名。
//报错
"use strict";
function fn(a, a) {
};
fn(1, 2);
3. 在不声明变量的情况下使用变量,是不允许的。
"use strict";
x = 3.14; // 会引发错误(x 未定义)
4. 对象也是变量,在不声明对象的情况下使用对象也是不允许的。
"use strict";
x = { p1: 10, p2: 20 }; // 这将引发错误
5. 删除变量(或对象)是不允许的。
"use strict";
var x = 3.14;
delete x; // 这将引发错误
6. 删除函数是不允许的。
"use strict";
function x(p1, p2) { };
delete x;// 这将引发错误
7. 不能使用with
with的注意事项!!!
- with在设置某个对象下的属性时,必须保证这个属性是存在的,不能新建,就可以修改。
- 如果不存在,意味着with定义了一个全局的变量。
with的使用案例:
// 不能使用with
var div = document.querySelector("div");
div.style.width = "50px";
div.style.height = "50px";
with (div.style) {
width = "50px";
height = "50px"
backgroundColor = "red"
}
案例:
var o = {
a: 1,
b: {
c: 3,
d: 4
}
}
with (o.b) {
c = 5;
d = 10;
e = 20;
}
console.log(o);
console.log(e);
8. 建议大家使用eval (反射),eval 的作用是将字符串反射为js语句。
console.log("3+4");//"3+4"
console.log(eval("3+4"));//7
9. 八进制数值文本是不允许的。
"use strict"; var x = 010; // 这将引发错误10. 使用转义字符是不允许的。
"use strict"; var x = \010;// 这将引发错误11.写入只读属性是不允许的。
"use strict"; var obj = {}; Object.defineProperty(obj, "x", {value:0, writable:false}); obj.x = 3.14;// 这将引发错误12.写入只能获取的属性是不允许的。
"use strict"; var obj = {get x() {return 0} }; obj.x = 3.14;// 这将引发错误13. 删除不可删除的属性是不允许的。
"use strict"; delete Object.prototype; // 这将引发错误14. 字符串 "eval" 和 "arguments" 不可用作变量。
"use strict"; var eval = 3.14;// 这将引发错误"use strict"; var arguments = 3.14;// 这将引发错误
1.3 在严格模式下this的指向
查看day10JS-this的使用情况。
1.4 执行函数的方式有哪些?
- 函数名(); ---> 例如:fn();
- new 函数名(); ---> 例如:new fn();
- 函数名.call(); ---> 例如: fn.call()
- 函数名.apply(); ---> 例如: fn.apply();
1.5 call()方法 /apply()方法
1. call(); 方法 /apply(); 方法 : 传入方法的第一个参数是Object对象时,this 指向的就是那个传入的Object对象。因为这两个方法在执行函数会将函数中 this 指向为call() /apply()方法中的第一个参数。这两个方法的作用就是改变this的指向。开不开启严格模式都不会影响this的指向。
function fn() {
console.log(this);
}
let object = { a: 1 };
fn.call(object);
fn.apply(object);
注意特殊情况!!!
非严格模式下
2. 传入call()方法的第一个参数是undefined或者是null,this指向的是window。
3. 传入call()方法的第一个参数是基本数据类型,会将传入的内容转换为第一个参数的对应的对象类型。this指向的是该基本数据类型的对象。
严格模式下
this将指向参数传入的内容。
非严格模式下:
// 非严格模式 : window
function fn(a, b) {
console.log(this, a, b);
}
fn.call(undefined, 1, 2)//window 1 2
fn.call(null, 1, 2)//window 1 2
//非严格模式下:会将传入的内容转换为对应的对象类型
fn.call(1)//Number undefined undefined
fn.call("a")//String undefined undefined
fn.call(true)//Boolean undefined undefined
严格模式下:
//严格模式 : this将指向参数传入的内容
"use strict";
function fn(a, b) {
console.log(this, a, b);
}
fn.call(undefined, 1, 2)//undefined 1 2
fn.call(null, 1, 2)//null 1 2
fn.call(1)//1 undefined undefined
fn.call("a")//a undefined undefined
fn.call(true)//true undefined undefined
1.6 call()方法 /apply()方法的区别
call()方法 :call传参是一个个传。例如: fn.call(object,1,2);
apply()方法 :apply传参传入数组。例如:fn.apply(object,[1,2]);
1.7 callee属性与caller属性
1. 在严格模式下不允许使用callee和caller。非严格模式下可以使用。
callee是arguments的一个属性,指向其函数自身。
2. caller返回一个函数的引用,这个函数里面调用了当前使用caller属性的函数。
使用这个属性要注意:
- 这个属性只有当函数在执行时才有用。
- 如果在javascript程序中,函数是由顶层调用的,则返回null。
非严格模式的案例:
//非严格模式
function fn() {
console.log(arguments.callee);//就是当前函数
}
fn();
非严格模式的案例: : callee的作用就是可以删除当前函数
document.addEventListener("click", function () {
document.removeEventListener("click", arguments.callee)
})
非严格模式的案例: : caller
function fn1() {
console.log(fn1.caller);//在全局中执行fn1,上下文环境的函数就是null
fn2();
}
function fn2() {
console.log(fn2.caller);//执行当前函数上下文环境中函数fn1
}
fn1();
2. 解构赋值
2.1 数组的补充知识
1. 数组的最后一个元素是空元素,那么它不作为数组的元素,length的长度不加上空元素的长度。
2. 空元素不等于undefined。
补充知识1的案例:
//补充知识1的案例:
var arr = [1, 2,];
console.log(arr);
console.log(arr.length);//2
补充知识2的案例:
var arr = [1, 2, , 3, undefined, 4];
console.log(arr.length);//6
注意!!!
1. forEach遍历数组不会遍历数组中的空元素,但会遍历数组中的undefined。
3. for循环遍历数组会遍历数组中的空元素,遍历结果空元素是undefined,也会遍历数组中的undefined。
var arr=[1,2,,3,,4];
arr.forEach(function(item){
console.log(item);//1 2 3 4
})
--------------------------------------
var arr=[1,2,,3,,4];
for(var i=0;i<arr.length;i++){
console.log(arr[i]);//1 2 undefined 3 undefined 4
}
-----------------------------------------
var arr=[1,2,,3,undefined,4];
arr.forEach(function(item){
console.log(item);//1 2 3 undefined 4
})
------------------------------------------
var arr=[1,2,,3,undefined,4];
for(var i=0;i<arr.length;i++){
console.log(arr[i]);//1 2 undefined 3 undefined 4
}
如何使用for循环去除数组中的空元素。使用in来判断这个属性是否是这个对象的属性。
var arr=[1,2,,3,undefined,4];
for(var i=0;i<arr.length;i++){
if(i in arr){
console.log(arr[i]);//1 2 3 undefined 4
}
}
2.2 解构赋值的用途与使用方法
解构赋值主要用于数组解构和对象解构。数组解构是按位解构, 对象解构是按属性名解构。
数组解构的使用方法:var/let [变量名1,变量名2, ...] = [值1,值2, ...];
对象解构的使用方法:var/let {变量名1,变量名2, ...} = {值1,值2, ...};
2.3 数组解构的案例
普通数组解构:
数组解构按位解构
let [a,b]=[1,2];//a=1,b=2
-------------------------
交换案例:
let a=1;
let b=2;
[b,a]=[a,b];
console.log(a,b);//a=1,b=1
-------------------------
let [a,b]=[1,2,3];
console.log(a,b);//a=1,b=2
-------------------------
let [a,b]=[1];
console.log(a,b);//a=1,b=undefined
-------------------------
// 给b设置初始值,如果b没有得到解构值,则是0
let [a,b=0]=[1];
console.log(a,b);//a=1,b=0
函数数组解构:
function fn([a,b=0]){
console.log(a,b);//a=1,b=0
}
fn([1]);
-------------------------------------
//这种赋值方式是由上面而得到的
function fn(a,b=0){
}
fn(1);
---------------------------------------
//这个解构赋值是 fn1=fn11这个函数,fn2=fn22这个函数,
let [fn1,fn2]=[
function(){//取个别名fn11
console.log("fn11");
},
function(){//取个别名fn22
console.log("fn22");
}
]
fn1();//fn11
fn2();//fn22
2.4 对象解构案例
对象解构按属性名解构,该属性名不存在那就是undefined。
普通对象解构:
// 对象解构
// 对象解构按属性名解构,该属性名不存在那就是undefined
let {a,c}={a:1,b:2};
console.log(a,c);//a=1,c=undefined
---------------------------------------------
//b有没有值?b没有值,因为b被重新二次分配给了{c,d},b不存在。
//b不存在就是未定义,会报错。但是a、c、d是存在的
let {a,b:{c,d}}={a:1,b:{c:3,d:4}};
console.log(a,b);//报错
//如果就想要b怎么办,那就不解构b
let {a,b}={a:1,b:{c:3,d:4}}
console.log(a,b);//a=1,b={c:3,d:4}
//如果a,b,c,d都想要怎么办,b一次不解构,一次解构
let {a,b:{c,d},b}={a:1,b:{c:3,d:4}};
console.log(a,b,c,d);//a=1,b={c:3,d:4},c=3,d=4
---------------------------------------------
// 可以设置默认值
let {a,b=0}={a:1}
console.log(a,b);//a=1,b=0
函数对象解构:在使用解构赋值作为函数参数传参,可以忽略函数参数先后顺序。
// 在使用解构赋值作为函数参数传参,可以忽略函数参数先后顺序
function fn({a,b,c=2}){
console.log(a,b,c);//a=1,b=2,c=2
}
fn({a:1,b:2});
注意!!!
函数的参数问题
函数传参的顺序必须严格按照这个顺序来写:必填参数,默认值参数,可选参数,剩余参数。
例如:function fn(a , b=2 , c , ...arg){ };
对象解构作为函数参数使用,可以避免参数传递的先后顺序问题。
对象解构中有相同属性名需要起别名,别名给入这个变量新的名字。
function fn({num,bool,fn}) {
//这种不牵扯到必填的问题
console.log(num,bool,fn);//num=1,bool=undefineed,fn=一个函数
}
fn({num:1,fn:function(){
}});
-------------------------------------------
//如果已经有了a这个属性名,我就给它取一个别名。
let {a,b:{a:a1}}={a:1,b:{a:2}};
console.log(a,a1);//a=1,a1=2
--------------------------------------------
// 如果已经有了a这个属性名,我就给它取一个别名并且赋初始值
//如果没有匹配到就使用初始值。
let {a,b:{a:a1=0}}={a:1,b:{}};
console.log(a,a1);//a=1,a1=0
----------------------------------------------
//如果后面没有b,那么b是有初始值为{a:3}
let {a,b:{a:a1=0}={a:3}}={a:1};
console.log(a,a1);//a=1,a1=3
-----------------------------------------------
//如果后面有b,所以不取初始值{a:3}
//但是b是一个空对象,b里面没有a1,所以a1取初始值0
let {a,b:{a:a1=0}={a:3}}={a:1,b:{}};
console.log(a,a1);//a=1,a1=0
-------------------------------------------------
let {a,b:{a:a1=0}={a:3}}={a:1,b:{a:10}};
console.log(a,a1);//a=1,a1=10
------------------------------------------------------
function fn({ a, b: { a: a1 = 0 } = { a: 3 } } = { a: 1, b: {} }) {
console.log(a, a1);
}
//没有传参,使用默认值
fn();//a=1,a1=0
// {a,b:{a:a1=0}={a:3}}是形参,{ a: 1 }是实参
// {a,b:{a:a1=0}={a:3}}={a:1}
// 后面没有b,所以取初始值{a:3}
fn({ a: 1 });//a=1,a1=3
// {a,b:{a:a1=0}={a:3}}={a:1,b:{a:10}}
fn({ a: 1, b: { a: 10 } });//a=1,a1=10
3. Set集合与Map映射
3.1 Array与Object
Array:是紧密结构,使用下标存储数据,存储的数据可以重复,无序存储(不会自动排序)可以手动排序,不关心key只关心存储的值。查询速度慢、添加删除速度慢 。
Object : 是松散结构,以键值对形式(key->value)存储,存储key不能重复,value值是可以重复,无序存储并且不能排序,查找速度快,可以直接根据key找到对应value值,添加删除速度快。无法获取对象的数据长度。key要求必须是字符串或者symbol,如惠不是就会自动隐式转换为字符串 。
3.2 Set集合与Map映射
Set集合:松散结构,没有key,只有value的集合,数据不能重复(唯一),不能排序插入删除速度快,可以得到集合中数据的长度,可以遍历,查找速度快 。
Map映射:松散结构,键值对形式存储,查找和添加删除速度快,不能排序,可以获取到数据存储的长度,key可以是任何类型 松散结构,键值对形式存储,查找和添加删除速度快,不能排序,可以获取到数据存储的长度,Key可以是任何类型。
3.3 WeakSet集合与WeakMap映射
WeakSet:弱引用集合。
WeakMap:弱引用映射。
3.4 迭代器
- HTMLCollection
- NodeList
- arguments
- Array
- Set本身也是迭代器。
4. Set集合的方法
let s = new Set([1,2,3,4])
console.log(s);
- add() : 添加。如果添加相同的value值是不会重复添加的。
- delete() : 删除。
- has() :判断这个值在集合中是否存在,返回值是布尔值。
- clear() : 清除集合中所有的数据。
- size属性:Set集合没有length,但可以使用.size属性查看Set集合中数据的多少。
let s=new Set([1,2,3,4]);
// set的增加方法不重复value值,使用添加
s.add(5);
s.add(5);
console.log(s);//Set(5) {1, 2, 3, 4, 5}
s.delete(5)//删除
console.log(s);//Set(4) {1, 2, 3, 4}
//判断这个值在集合中是否存在,得到布尔值
console.log(s.has(5));//false
//清除所有数据
s.clear();
console.log(s.clear());//undefined
console.log(s);//Set(0) {}
//数据的多少,没有length,只能通过size获取
console.log(s.size);//0
Set集合的遍历方法:
1. forEach
2. for of
let s=new Set([1,2,3,4]);
//方法一:
s.forEach(function(value){
console.log(value);//1 2 3 4
})
//方法二:
for(var value of s){
console.log(value);//1 2 3 4
}
5. Set集合的使用场景
5.1 去重
var arr=[1,2,3,1,2,3,4,5,7,6,4,3,2,4,6,5,7];
//把数组arr放到Set集合中,Set集合去重,在使用数组的from()方法把Set集合转成数组
var arr1=Array.from(new Set(arr));
console.log(arr);//[1, 2, 3, 1, 2, 3, 4, 5, 7, 6, 4, 3, 2, 4, 6, 5, 7]
console.log(arr1);//[1, 2, 3, 4, 5, 7, 6]
console.log(arr1===arr);//false
使用Set去重有缺点就是引用地址会改变,去重过后的数组与原来的数组不是同一个。所以去重看实际开发的要求来使用。
5.2 减少查找
6. WeakSet集合
1. undefined与null的区别:undefined只是开辟了空间没有赋值。null的作用就是清除引用关系,方便垃圾回收机制回收。
2. 强引用关系:先将对象的引用地址赋值给数组,在将对象设置为null,引用列表就清除该对象的引用,但是有数组存储着它的引用地址,垃圾回收车不会清除这个数据,数组中存储的对象引用地址是强引用存储。
强引用关系的案例:
// 强引用关系
var o = { a: 1 };
let s = new Set();
s.add(o);
o = null;
console.log(s);
3. 弱引用关系:先将对象的引用地址赋值给数组,引用列表清除了该对象的引用并且没有其它对象引用,垃圾回收车才会清除这个数据,数组中存储的对象引用地址是弱引用存储。WeakSet集合与WeakMap映射是弱引用地址,其它都是强引用地址。
弱引用关系的案例:
// 弱引用关系
var o = { a: 1 };
let s = new WeakSet();
s.add(o);
o = null;
console.log(s);
代码的运行结果有两种:一种是及时被垃圾回收机制清除了,一种是还没有被垃圾回收机制清除。
原因是:垃圾回收机制是过了一定的时间才启动,垃圾回收机制启动时间是不可控制的。