ES5
一.严格模式
什么是严格模式: 比普通的js运行机制要求更严格的模式;
如何使用严格模式:只要在当前作用域的顶部添加 "use strict";
(1)禁止给未声明的变量赋值;
旧JS中:如果给未声明的变量赋值,结果会自动在全局创建变量;
严格模式中:如果给未声明的变量赋值,结果会报错;
例子:
"use strict"
function send(){
msg="启用严格模式会报错"
console.log(msg)
}
send()
(2)静默失败升级为错误:执行不成功,但也不报错
"use strict"
var eric={
eid:"1001",
ename:"埃里克"
}
//定义属性eric的eid为只读属性,不可以修改
Object.defineProperty(eric,"eid",{
writable:false
})
//公司要求员工编号
eric.eid=-1;
console.log(eric);
报错:Uncaught TypeError: Cannot assign to read only property 'eid' of object '#<Object>'
不能赋值给只读属性eid
(3).普通函数调用中的this不再指向window,而是指向undefined
旧JS中:普通函数调用中的this->window——全局污染
严格模式:普通函数调用中的this不再指向window,而是指向undefined——堵住了因为错误使用this而导致全局污染的可能,也极其便于程序的调试
"use strict"
function Student(sname, sage){
this.sname=sname;//报错: 无法访问undefined的sname属性
this.sage=sage;
//没有return
}
var lilei=new Student("Li Lei",11);
console.log(lilei);
var hmm=Student("Han Meimei",12);
console.log(hmm);//undefined
console.log(window.sname);//"Han Meimei"
console.log(window.sage);//12
(4). 禁用arguments.callee
aguments.callee 函数在调用时获得当前调用函数名的关键词
问题:调用递归时 如果函数内写死当前函数名 会形成紧耦合 外部改动里面就会报错
结果:aguments.callee 来替代写死的函数名
何时:专门为递归使用,代替函数名
原因:因为严格模式为了不推荐递归算法,因为递归计算量大,效率低 所以禁止使用arguments.callee
// 斐波那契数列 禁用递归
function f(i){
if(i<3){
return 1;
}else{
// return f(i-1)+f(i-2); 紧耦合
return arguments.callee(i-1)+arguments.callee(i-2);
}
}
console.log(f(10)) //55
二.保护对象
对象本身没有自保能力,可随意给对象增,删,改 但是对象的属性有一些特殊要求,比如只读
问题: 旧js中 对象的属性 仅仅是一个存在对象中的普通的变量而已 没有任何自保能力
解决: 从ES5标准开始,对象的属性已经不仅仅是一个简单的变量了。而是一个缩微的小对象
查看属性的缩微对象:
var obj=Object.getOwnPropertyDescriptor(对象,"属性名")
例子:
var james={
eid:23,
ename:"詹姆斯"
}
var obj=Object.getOwnPropertyDescriptor(james,"eid");
console.log(obj)
/*configurable: true //控制是否可以删除当前属性 还控制是否可以修改开关
enumerable: true //控制是否可以遍历
value: 23 //实际替属性来储存属性值
writable: true*/ //控制是否修改
修改属性内部的开关:Object.defineProperty(对象,"属性名",{开关})
var james={
eid:23,
ename:"詹姆斯"
}
Object.defineProperty(james,"eid",{
writable:false, //不可以修改;
enumerable:false, //不可以遍历
configurable:false //不可以删除,不可以修改上面的两个按钮
})
james.eid="6"
console.log(james) //修改不掉
修改多个属性开关: Object.defineProperties(对象名,{属性名1:{开关},属性名2:{开关}})
var james={
eid:23,
ename:"詹姆斯"
}
Object.defineProperties(james,{
eid:{
writable:false,
enumerable:false,
configurable:false
},
ename:{
writable:false,
enumerable:false,
configurable:false
}
})
james.ename='勒布朗'
james.eid=26
console.log(james)
问题:设定的开关只存在开启和关闭,没有灵活的编写自定义的规则;
解决:访问器属性
三.访问器属性 get() set()
自己不储存值,而是提供对另一个属性的保护的特殊属性
如果希望自定义规则保护属性时,就用访问器属性
1.将要保护的属性半隐藏
2.访问器属性的名字,必须和要使用的属性名一致,才能起到冒名顶替的作用
3.get()获取操作,set()修改操作
4).当其它程序试图获取属性值时: 对象.属性名
i. 会自动调用访问器属性中的get()方法
ii. get()方法会自动去受保护的半隐藏的属性中获取当前的属性值
iii. get()再将当前获得的属性值返回给其它程序的调用者
2). 当其它程序试图修改属性值时: 对象.属性名=新值
i. 会自动调用访问器属性中的set(value)方法
ii. 自动将等号右边的新值传递给形参value变量
iii. 在set()内部,先验证value接住的新值是否符合要求
iv. 如果验证通过,则set()会将value的新值保存到受保护的半隐藏的另一个属性中
v. 如果验证不通过,则set()不但不会保存value的新值到受保护的属性中,而且还会报错
示例:
var james={
eid:23,
ename:"詹姆斯"
}
Object.defineProperties(james,{
_eid:{//将要保护的属性半隐藏
value:james.eid, //从现在james对象的旧属性eid中放到这里保护起来
writable:true,
enumerable:false,
configurable:false
},
eid:{ 访问器属性的名字,必须和要使用的属性名一致,才能起到冒名顶替的作用
get:function(){
return this._eid;
},
set:function(value){ // //value ← 要修改的新属性值
if(value>=18 && value<=60){ //先验证value 如果验证通过,this._属性名=value
this._eid=value;
}else{
throw Errow // 否则如果验证未通过,不但不保存新属性值,还会报错
}
},
enumerable:true,
configurable:false
}
})
james.eid=21
console.log(james)
四.保护对象结构
虽然保护住了现有的属性,但是无法防止对对象结构的破坏 比如: 随意添加新属性
(1)防拓展 Object.preventExtensions(对象)
//示例:阻止对象添加新属性
"use strict"
var james={
eid:23,
ename:"詹姆斯"
}
Object.preventExtensions(james);
james.ball="篮球"
console.log(james) //Uncaught TypeError: Cannot add property ball, object is not extensible
(2)密封 Object.seal(对象)
问题:恶意删除对象中的某个属性,虽然现在我们给每个属性都手工加了configurable:false,但是如果要防止删除的属性很多,configurable:false就要写很多遍 繁琐
密封: 既禁止添加新属性preventExtensions(),又禁止删除现有属性
解决:Object.seal(对象)
原理:(1)自动调用了Object.preventExtensions(对象) (2)自动给每个属性添加configurable:false
(3)冻结 Object.freeze(对象)
既禁止添加删除属性,又禁止修改属性值
原理:
i. 也会自动调用preventExtensions()阻止添加新属性
ii. 也会自动添加configurable:false 禁止删除现有属性
iii. 还会自动修改所有属性的writable:false 让所有属性只读
示例:
"use strict";
var pool={
host:"127.0.0.1",
port:3306,
}
//禁止修改pool中任何属性值
Object.freeze(pool);
//试图添加新属性
// pool.uname="root";
//试图删除现有属性
// delete pool.port;
//试图修改属性值
// pool.host="192.168.0.100"
五.object.create()
专门用于在没有构造函数的情况下也能创建子对象继承父对象。
何时: 如果没有构造函数,也想创建子对象,继承一个现有父对象时,就用Object.create()\
(1)简单:仅创建空对象,继承父对象
var 子对象= Object.create(父对象)
//创建一个新的空对象,并自动让新的空对象继承指定父对象——其实完成的就是new的前两步。
//示例:
var user={
sal:99999,
car:'benz'
}
var lilei=Object.create(user)
console.log(lilei)
(2)高级:既创建空对象 又继承父对象 还能给新对象添加自有属性
var 子对象=Object.create(父对象, {
//添加自有属性,必须使用和defineProperties相同的格式
属性名:{
value:属性值,
开关: true或false,
... : ...
},
属性名:{
value:属性值,
开关: true或false,
... : ...
},
})
六.替换this
call() apply() bind()
有些时候函数执行时,其中的this不是我们想要的;
临时替换this:call()
1). 调用函数
2). 临时替换函数中的this为call()中第一个实参值对象
3). 将call()中第二个实参值及其之后的所有剩余实参值传给正在调用的函数作为函数的实参值。
//示例:
function sal(base,bonus1,bonus2){
console.log(`${this.ename},的工资:${base+bonus1+bonus2}`)
}
var lilei={ename:"lilei"};
var hanm={ename:"Hanmei"};
// 错误的做法;
//lilei.sal() //lilei.sal is not a function
//正确做法
sal.call(lilei,10000,999,888)
临时替换this:apply()
将来传给要调用的函数的多个实参值可能是放在一个数组中的,但是函数本身却需要多个实参值单独传入
解决: ES5中专门提供了既能替换this,又能打散数组的函数:apply
apply原理: 4件事:
1). 调用函数
2). 临时替换函数中的this为apply()中第一个实参值对象
3). 先打散apply()的第二个实参值数组为多个实参值
4). 再将打散后的多个实参值传给正在调用的函数作为函数的实参值。
//示例:
function sal(base,bonus1,bonus2){
console.log(`${this.ename},的工资:${base+bonus1+bonus2}`)
}
var lilei={ename:"lilei"};
var hanm={ename:"Hanmei"};
var arr=[10000,2000,999]
sal.apply(lilei,arr) //arr会被打散 分别传给形参
永久绑定this的办法: bind
(1). var 新函数=原函数.bind(替换this的对象)
a. 基于原函数,创建一个一模一样的新函数副本
b. 将新函数副本中的this永久替换为指定的对象
(2).新函数(只传函数需要的实参值即可,不用传替换this的对象)
如何永久绑定this和个别固定的实参值
(1). var 新函数=原函数.bind(替换this的对象, 可固定的实参值,...)
(2). 新函数(只需要传入剩余可能变化的实参值,已经固定个实参值不用传)
示例:
function sal(base,bonus1,bonus2){
console.log(`${this.ename},的工资:${base+bonus1+bonus2}`)
}
var lilei={ename:"lilei"};
var hanm={ename:"Hanmei"};
// 永久绑定this
var sal1=sal.bind(lilei,10000)
sal1(1000,2000)
数组函数
1.判断;
(1)every() 判断数组是否所有元素都符合条件
var bool=数组.every(function(当前元素值,当前下标位置,当前数组){
return 判断条件
})
原理:
(1).every()自带for循环遍历;
(2).每遍历到元素自动执行回调函数,自动传入三个值;
(3).elem 接住当前正在遍历到的元素
(4).i 接住当前正在遍历下标的位置
(5).arr 当前正在遍历的整个数组对象
返回值:如果数组中所有元素都符合要求返回true,如果有不符合要求的元素返回false
示例:
var arr1=[1,2,3,4,5];
var arr2=[2,4,6,4,2];
// 判断那个数组全由偶数组成:
var result1=arr1.every(function(elem,i,arr){
return elem%2==0
})
console.log(result1) //false
var result2=arr2.every(function(elem,i,arr){
return elem%2==0
})
console.log(result2) //true
(2)some() 判断数组是否包含符合条件的元素
var bool=数组.some(function(当前元素值, 当前下标位置, 当前数组){
return 判断条件
})
原理:
1). some内自带for循环遍历,遍历数组中每个元素
2). 每遍历到一个元素就自动执行一次回调函数。
3). 在执行回调函数时,自动给回调函数传入三个值:
i. elem: 接住当前正在遍历到的一个元素值
ii. i: 接住当前正在遍历到的下标位置
iii. arr: 接住当前正在遍历的整个数组对象。
4). 在回调函数内,判断当前元素值elem是否符合条件。并返回判断结果给some()函数
5). some()函数接到判断结果后:
i. 如果当前元素的检查结果返回false,则继续遍历下一个元素
ii. 如果当前元素的检查结果返回true,则直接退出循环,不再遍历下一个元素。整个some()返回true
iii. 除非所有元素的检查结果都为false,整个some()才返回false
返回值:
1). 只要数组中有一个元素符合要求,则some()就返回true
2). 除非数组中一个符合要求的元素都没有,则some()才返回false
遍历
forEach():仅简化对数组的普通遍历操作
问题:
1). for循环可以遍历数组,没有简化的空间
2). for in循环一般不用来遍历数字下标的索引数组 只用来遍历自定义下标名称的对象或关联数组
使用:
数组.forEach(function(当前元素值, 当前下标位置, 当前数组){
对当前元素值做任何操作
});
强调:
forEach中的回调函数不需要return
原理:
1). 自带for循环,自动遍历数组中每个元素
2). 每遍历到一个元素就自动执行一次回调函数。
3). 在执行回调函数时,自动给回调函数传入三个值:
i. elem: 接住当前正在遍历到的一个元素值
ii. i: 接住当前正在遍历到的下标位置
iii. arr: 接住当前正在遍历的整个数组对象。
示例:
var arr=["詹姆斯","戴维斯","哈雷尔"]
arr.forEach((elem,i,arr)=>{
console.log(`${elem},是湖人队的球员`)
})
//结果
詹姆斯,是湖人队的球员
戴维斯,是湖人队的球员
哈雷尔,是湖人队的球员
map():制出原数组中每个元素值,执行相同操作后,放入一个新数组中返回.
何时:
如果想基于原数组,修改后,生成一个新数组,原数组保持不变。都用map
使用:
var 新数组=原数组.map(function(当前元素值, 当前位置, 当前数组){
return 将当前元素修改后的新元素值
})
意为:
遍历出原数组中每个元素值,修改后,放入一个新数组中返回
原理:
0). 自动创建一个新的空数组
1). 自带for循环,自动遍历数组中每个元素
2). 每遍历到一个元素就自动执行一次回调函数。
3). 在执行回调函数时,自动给回调函数传入三个值:
i. elem: 接住当前正在遍历到的一个元素值
ii. i: 接住当前正在遍历到的下标位置
iii. arr: 接住当前正在遍历的整个数组对象。
4). 在回调函数内根据当前元素的旧值,修改出一个新元素值,通过return返回给map函数
5). map函数拿到每次回调函数返回的新元素值之后,都会自动将新元素值添加到新数组中当前相同位置上!
示例:
var arr=[1,2,3,4,5];
//想将原数组中每个元素值x2,然后放入新数组中返回。原数组保持不变
var arr2=arr.map(function(elem,i,arr){
console.log(
return elem*2;
})
console.log(arr2);//[2,4,6,8,10]
console.log(arr);//[1,2,3,4,5]
过滤
复制出数组中符合要求的元素组成新数组
使用:
var 新数组=原数组.filter(function(当前元素,位置,当前数组){
return 判断条件
})
原理:
a. 自动创建一个新的空数组
b. 自带for循环,自动遍历数组中每个元素
c. 每遍历到一个元素就自动执行一次回调函数。
d. 在执行回调函数时,自动给回调函数传入三个值:
1). elem: 接住当前正在遍历到的一个元素值
2). i: 接住当前正在遍历到的下标位置
3). arr: 接住当前正在遍历的整个数组对象。
e. 在回调函数当前只判断当前元素值是否符合要求并返回判断结果
示例:
var arr=[1,2,3,4,5];
//想过滤出数组中的偶数
var arr2=arr.filter(function(elem,i,arr){
console.log(`arr.filter调用了一次回调函数。自动传入elem:${elem},i:${i},arr:${arr}。回调函数返回${elem%2==0}。`)
return elem%2==0
})
console.log(arr2);//[2,4];
console.log(arr);//[1,2,3,4,5];
汇总
对数组中的所有元素进行统计,得出一个最终结论
使用:
var 结果=数组.reduce(
function(临时汇总值, 当前元素, 当前位置, 当前数组){
return 将当前元素值累加到临时汇总值上
},
起始值
);
原理:
a. 创建一个变量保存起始值。
b. 自带for循环,自动遍历数组中每个元素
c. 每遍历到一个元素就自动执行一次回调函数。
d. 在执行回调函数时,自动给回调函数传入四个值:
1). box: 接住截止到当前元素之前的所有元素的临时汇总值
2). elem: 接住当前正在遍历到的一个元素值
3). i: 接住当前正在遍历到的下标位置
4). arr: 接住当前正在遍历的整个数组对象。
e. 在回调函数内部,将新元素值累加到临时汇总值变量,并返回新的汇总值
f. reduce()函数接到回调函数返回的新汇总值之后,会再保存到变量中,为下一次累加做准备。
g. 当整个数组遍历结束后,变量中就保存了数组中所有元素的最终汇总值
h. reduce()将最终的汇总值返回到外部,保存到外部的变量中。
示例:
var arr=[1,2,3,4,5];
var result=arr.reduce(
function(box,elem,i,arr){
return box+elem;
},
)
console.log(result);//15