ES5
- 严格模式
-
概念:比普通js运行机制要求更严格的新运行机制
-
启用严格模式:在当前作用域的顶部:
"use strict"
-
新要求:
(1)禁止给为声明过的变量赋值
(2)静默失败升级为错误:旧js中,执行不成功但不报错,在严格模式下,会报错
(3)普通函数调用中的this不再指向window,而是指向undefined
若函数调用时,前边既没有.也没有new,则函数中的this改为指向undefined
(4)禁用了arguments.callee
概念:arguments.callee:在调用函数时,函数内部自动获取当前函数本身的关键词
原因:arguments.callee解决了递归调用中的紧耦合问题,但因为递归算法本身存在大量重复的计算,效率极低,所以arguments.callee被禁用。
解决:绝大多数的递归都使用循环来代替
-
使用arguments.callee实现斐波那契数列
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
"use strict";
//斐波那契数列:
//1 1 2 3 5 8 13 21 34 55
//1 2 3 4 5 6 7 8 9 10
//前两个数都是1
//从第三个数开始: 每个数都是相邻的前两个数的和
//定义函数,计算出数列中第n个数是几?
function fib(n){
//如果n<3,直接返回1
if(n<3){
return 1
}else{//从第三个数开始: 每个数都是相邻的前两个数的和
//n位置的数=n-1位置的数+n-2位置的数
//所以,想算出fib(n),必须先算出fib(n-1)和fib(n-2)
return arguments.callee(n-1)+arguments.callee(n-2)
}
}
console.log(fib(10))//55
</script>
</body>
</html>
运行结果:
Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
at fib (7_use_strict4.html:25)
at 7_use_strict4.html:28
因为禁用arguments.callee所以报错
-
保护对象
专门保护对象的属性和结构的机制- 保护属性
(1)每个属性底层都是一个缩微的对象
(2)获得属性底层的对象结构:var 变量名 = Object.getOwnPropertyDescriptor(对象名,"属性名")
举例:获取eric对象
- 保护属性
var eric={
name:"ysx",
age:25,
hobby:"演戏"
}
var eric = Object.getOwnPropertyDescriptor(eric,"name");
console.log(eric);
(3)修改一个属性中的开关Object.defineProperty(对象名,"属性名",{开关名:true/false, ... : ... , })
属性底层结构放大:
name:{
value:"ysx",//替当前属性保存属性值
writeable:true,//控制是否可以修改当前属性值
enumberable:true,//控制是否可用for遍历当前这个属性值,只防for,不防.
configurable:true//控制是否可以删除当前属性,控制是否可以修改前两个开关
}
举例:修改属性中开关的状态进行测试
"use strict";
var eric={
name:"ysx",
age:25,
hobby:"演戏"
}
var ericName = Object.getOwnPropertyDescriptor(eric,"name");
console.log(ericName);
Object.defineProperty(eric,"age",{
writable:false
})
//修改了name属性为不可修改
//尝试修改name属性
eric.age = -1;
console.log(eric);
console.log(eric.age)
在严格模式下,会报错 静默升级
在非严格模式下:不会改变属性值
再打印当前的属性开关情况
var ericAge = Object.getOwnPropertyDescriptor(eric,"age"); console.log(ericAge);
(4)修改多个属性的开关
Object.defineProperties(对象名,{属性名:{开关名:true/false, ... : ... },属性名:{... : ... }})
- 访问器属性(用自定义规则保护属性)
自己不保存属性值,而是仅提供对另一个数据属性的保护
(1)定义访问器属性
举例:使用访问器属性保护员工年龄介于18~50之间
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var eric={
ename:"埃里克",
eage:25
}
//开始保护多个属性
Object.defineProperties(eric,{
//创建_eage属性,将eage属性中的值转移,并隐藏_eage
_eage:{
value:eric.eage,
writable:true,
enumberale:false,
configurable:false
},
//将原属性变成一个 访问器属性 作为真正属性的保镖
eage:{
//2个保镖,一个从_eage中读取数据返给外部,一个接收外部的数据进行判断
//当set保镖,判断完毕,符合条件,返给_eage,不符合条件,报错
//get保镖
get:function(){
return this._eage;
},
//get结束
//set保镖
set:function(value){
if(value>=18 && value<=50){
this._eage = value;
}else{
throw error("年龄out");
}
},
//set结束
enumberable:true,
configurable:false
}
})
console.log(eric);
//尝试修改eage的值
// eric.eage = 20;
console.log(eric.eage);
</script>
</body>
</html>
- 保护结构
阻止用户向对象添加新属性或删除现有属性
ES5提供了专门保护对象的办法:3个级别
(1)防扩展 – 禁止当前对象强行添加新属性Object.preventExtensions(对象名)
(2)密封 – 既禁止对象添加属性,又禁止删除现有属性Object.seal(对象名)
1). 自动调用了preventExtentsions()
2). 自动修改了每个属性的configurable为false
(3)冻结 – 禁止删除属性,禁止修改属性,禁止添加属性
1).自动调用了preventExtentsions()
2).自动修改了每个属性的configurable为false
3).自动修改每个属性的writable属性为false
-
Object.create
(1)在没有构造函数的情况下,也想创建子对象,继承指定的父对象,用Object.create()
(2)var 子对象 = Object.create(父对象,{属性名:{开关名:true/false}}//添加属性)
-
替换this
当函数中的this不是我们想要的时,替换this
(1)call() – 临时替换this
函数用来封装了方法,当有不同的对象重复使用方法时,临时替换this
call()内的第一个实参替换this
举例:2个员工使用同一个函数计算工资
function suan(base,reward1,reward2){
let sum = base + reward1 + reward2;
console.log(`${this.ename}的总工资为: ${sum}`);
}
var xiaoming = {ename:"Xiaoming"};
var Hanme = {ename:"Hanmei"};
//员工分别计算自己的工资
suan.call(xiaoming,5000,200,300);
(2)apply() – 当传参数较多,且为以数组传递时
apply()内的第一个实参替换this
举例:将员工的底薪,存在数组中,计算员工总工资
function suan(base,reward1,reward2){
sum = base+reward1+reward2;
console.log(`${this.ename}的总工资为:${sum}`);
}
var Hanmei = {ename:"Hanmei"};
var arr = [5000,600,630];
suan.apply(Hanmei,arr);
原理:
(3)函数副本
为对象创建一个专属的函数副本,并永久绑定this和部分实参值
var 函数副本 = 原函数.bind(替换的this对象,部分固定的实参值)
举例:为韩梅创建一个专属的jisuan()函数,并永久绑定this和底薪
function jisuan(base,reward1,reward2){
sum = base+reward1+reward2;
console.log(`${this.ename}的总工资是:${sum}`);
}
var Hanmeimei = {ename:"Hanmei"};
var Han = jisuan.bind(Hanmeimei,5000);
Han(500,600);
当反复使用一个函数,并永久替换this时,用bind()
- 数组函数(4类6个) —— 都自带for循环
判断(2个) 遍历(2个) 过滤(1个) 汇总(1个)
(1)判断数组函数 —— 返回的结果是true/false
1)every() —— 判断数组中是否所有元素都符合要求
var bool=arr.every(function(elem,i,arr){
return 判断条件
})
elem:当前正在遍历的元素
i:当前遍历到的位置
arr:当前正在遍历的数组
当返回false则不再遍历,表示当前数组不是所有元素都符合条件
遍历到最后返回true,表示当前数组所有元素都符合条件
2)some() —— 判断数组中是否包含符合要求的元素
var bool = arr.some(function(elem,i,arr){
return 判断条件;
});
当遇到一个符合要求的元素,即返回true,不再循环
当循环到最后,返回false,则表明该数组内没有符合条件的元素
(2)遍历数组函数 —— 返回的是遍历结果
1)forEach() —— 遍历数组中每个元素值,执行相同的操作
arr.forEach(function(elem,i,arr){
对当前元素值执行操作
});
举例:喊到
arr=["小A","小B","小C","小D"];
arr.forEach((elem,i,arr)=>{console.log(`${elem}到!`)})
2)map() —— 依次取出原数组的每个元素值,执行相同操作后,放入新数组返回
var 新数组 = arr.map(function(elem,i,arr){
return 根据当前元素值加工后的一个新值
});
举例:取出原数组的值加10再返回新数组
var arr1 = [1,2,3,4,5];
var arr2 = arr1.map((elem,i,arr1)=>{
return elem += 10;
});
console.log(arr2);
(3)过滤数组函数 —— 仅复制出原数组中符合条件的元素值,放入新数组返回
var 新数组 = arr.filter(function(elem,i,arr){
return 判断条件;
});
举例:从数组中取出所有偶数
var arr1 = [1,2,3,4,5,6];
var arr2 = arr1.filter((elem,i,arr1)=>{
return elem%2 == 0;
});
console.log(arr2);
注意:filter()后,原数组保持不变~
(4)reduce() —— 对原数组中的每个元素值进行统计,最终得出一个统计结果
var 结果 = arr.reduce(function(box,elem,i,arr){
return box+elem;
},起始值);
box:临时汇总值
类似捐款箱,box的值是捐款箱里就有的钱
举例:统计一个数组中所有元素值的和
var arr1 = [10,20,30,40];
var total = arr1.reduce(function(box,elem,i,arr1){
return box+elem
},500);
console.log(total);