第三章*****************************************************************************************
1.ES5:
ECMAScript: ECMA组织制定的JavaScript语言的国际标准,规定了JS语言的核心语法
ES5是ECMAScript的第五个版本(第四版过于复杂废弃了),IE8部分此版本
保护对象:
什么是:① 保护对象的属性和属性值始终有意义
② 防止篡改对象的结构
为什么:① 对象的属性,默认可随意赋值
② 对象可随意添加、删除属性
何时: 严格来说,今后所有对象,都要有自我保护的抵抗力
如何: 分为保护属性和防篡改
保护属性: 保护对属性值的修改
对象属性分为:
命名属性: 可通过.直接访问的属性
分为: 数据属性: 直接保存属性值的属性
访问器属性: 不直接存储属性值,仅提供对其他数据属性的保护
内部属性: 无法通过.直接访问的属性
数据属性的四大特性:
value:实际存储属性值
writable:true/false控制属性是否可修改属性值
enumerable:true/false控制属性是否可被for in遍历(false会跳过),仅控制遍历,无法控制用.访问
configurable:true/false控制是否可删除该属性,控制是否可修改其它两个特性
强调: configurable经常作为前两个属性的双保险,且一旦设为false,不可逆
获取四大特性:
Object.getOwnPropertyDescriptor(obj,"属性名")
设置1个属性的四大特性:
Object.defineProperty(obj,"属性名",{
要修改的特性:特性值,
要修改的特性:特性值,
... ...
})
问题: defineProperty一次只能修改一个属性
解决: 同时修改多个属性的特性:
Object.defineProperties(obj,{
属性名:{ 要修改的特性 },
属性名:{ 要修改的特性 },
... : ...
})
添加属性: 可用defineProperty添加新属性
只要defineProperty要修改的属性不存在,就会自动添加
强调: 用defineProperty添加的新属性,四大特性默认值都为false
而用.添加的新属性,四大特性默认值都为true
问题: 无法使用自定义逻辑保护属性
解决: 访问器属性
_______________________________________________________________________________________________
访问器属性: 不直接存储属性值,仅提供对其他数据属性的保护
何时: 只要使用自定义的规则保护属性值
为什么: 数据属性的四大特性,保护规则是固定的,无法自定义
如何定义: 2步:
①定义一个隐藏的数据属性,用于实际存储属性值
②再定义一个访问器属性,保护隐藏的数据属性:
Object.defineProperty(obj,"属性名",{/*或用defineProperties,不能通过直接量或.创建*/
get(){ /*在试图获取属性值时自动调用*/
return this.隐藏的数据属性; /*返回受保护的数据属性值*/
},
set(val){ /*在试图修改属性值时自动调用,参数val会自动获得要修改的新值*/
if(...) /*验证val是否符合规则*/
... /*将val赋值给受保护的属性*/
else ... /*否则*/
throw new Error /*主动抛出错误*/
},
enumerable:true, /*设置四大属性*/
configurable:false
})
如何使用: 只要定义了访问器属性,就优先使用访问器属性,而不用受保护的数据属性
访问器属性的用法和普通属性用法完全一样
但是: 在取值时,自动调用get()
在赋值时,自动调用set(),val会自动获得要赋的新值
内部属性: 无法通过.直接访问的隐藏属性
比如: __proto__
_______________________________________________________________________________________________
防篡改: 阻止对对象结构的修改
3个级别:
①防扩展: 禁止添加新属性
Object.preventExtensions(obj)
原理: 每个obj内部都有一个隐藏属性: Extensible,默认为true
preventExtensions将obj的Extensible改为false
②密封: 在防扩展的基础上,进一步禁止删除现有属性(两个不要一起用)
Object.seal(obj)
原理: 将obj的Extensible改为false
将所有属性的configurable都自动改为false
③ 冻结: 在密封基础上禁止修改一切属性值(过于严格)
Object.freeze(obj)
原理: 修改obj的Extensible为false
将所有属性的configurable都改为false
还将所有属性的writable都改为false
_______________________________________________________________________________________________
Object.create(): 本质上是创建一个新的子对象
什么是: 基于一个现有父对象,创建一个新的子对象继承当前父对象,并扩展新属性
何时: 今后如果没有构造函数的情况下,也想创建子对象
如何:
var child=Object.create(father,{ /*若不用扩展自有属性,father后的内容不必写*/
自有属性:{
value:值,
writable:true,
enumerable:true,
configurable:true,
},
... : {
...
}
});
强调: 只要添加到对象中的属性,四大特性默认为false,必须显式写为true
原理:① 创建一个空对象
② 让新对象自动继承father
③ 为新对象扩展新的自有属性
_______________________________________________________________________________________________
call/apply/bind:
共同:为了替换函数中不想要的this
何时: 只要函数中的this不是想要的 (函数不加括号,只在call/apply/bind右边加括号)
call和apply:
什么是:强行调用一个函数并临时替换函数中的this为指定的新对象
call: 要求传入函数的参数必须单独传入,逗号分隔
第1个参数是要指定的this,第2,3...个是要替换的参数
apply: 要求传入函数的参数,必须放入数组中整体传入
apply可自动打散数组类型的参数,单个传入
第1个参数是要指定的this,第2个参数是一个数组
bind: 基于一个现有函数创建一个功能完全相同的新函数,并永久绑定this为指定对象
还可永久绑定部分固定的参数值
替换回调函数中的this时,都用bind
比如: var newFun=fun.bind(obj) //接收新参数,函数后添加
newFun(参数1,...); //调用函数
强调: 被bind永久绑定的this,不能再被call/apply临时替换
_______________________________________________________________________________________________
数组API:
判断: 判断数组中的元素是否符合要求
返回值:bool(可判断是否满足条件)
①every: 判断数组中所有元素是否都满足要求
var bool = arr.every(function(val,i,arr){ //回调函数: 当前元素值:val;当前元素位置:i;arr:当前数组
return 判断条件
});
②some: 判断数组中是否包含满足条件的元素
var bool = arr.some(function(val,i,arr){
return 判断条件
});
强调:数组API的回调函数中this默认->window,所以,不能用this指代当前元素值,但可用如arr[i]或val
遍历: 对数组中每个元素执行相同的操作:
①forEach: 对原数组中每个元素执行相同的操作
arr.forEach(function(val,i,arr){ //没有返回值
arr[i]=新值; //对当前元素执行的操作,直接修改原数组,只能使用arr[i]
})
②map: 取出原数组中每个元素,执行相同操作后,再放入新数组返回
var arr2 = arr1.map(function(val,i,arr){ //返回一个新数组
return 对当前元素操作后的新值(放入新数组中) //不修改原数组,使用arr[i]=..可修改原数组
})
过滤和汇总:
过滤: filter: 复制原数组中符合条件的元素,组成新数组
var subArr=arr.filter(function(val,i,arr){
return 判断条件 //筛选出arr中符合判断条件(为true)的元素值,放入新数组返回
})
汇总: reduce: 将数组中所有元素,统计出一个汇总结果
var r=arr.reduce(function(prev,val,i,arr){ //prev: 截止目前的临时汇总值
return prev+val
},startVal); //startVal: 表示汇总开始的基数,可不写(默认为0)
将arr数组中每个值累加后,求和
强调: reduce不一定非要从0开始累加,可从任意startVal(也可以是其它数组)开始累加
_______________________________________________________________________________________________
严格模式:
什么是: 比普通js运行模式要求更严格的运行机制
为什么: 解决普通js运行模式中广受诟病的缺陷
何时: 今后都要在严格模式下开发
① 新项目, 必须全部启用严格模式
② 旧项目, 逐个函数向严格模式迁移
如何: 2种:
① 整个代码段启用严格模式:
在<script>标签或js文件的开头加入:"use strict";
② 仅对单个函数启用严格模式
仅在function内,函数体的顶部加入:"use strict";
要求:
1. 不允许对未声明的变量赋值
2. 静默失败升级为错误
3. 不推荐使用arguments.callee来实现递归
_______________________________________________________________________________________________
2.ES6(框架广泛采用,又称为ECMAScript 2015):
模板字符串: 对字符串拼接的简化(2015年首次发布,IE Edge不支持)
何时: 如果字符串中包含需要动态执行的表达式或回车换行时
如何:3件事
① 用``反引号(ESC键的正下方)包裹字符串
② 字符串中的变量和表达式都要放在${...}中
③ 模板字符串中支持: 换行,变量,表达式(),注释也会被解析
let:
1. 专门声明仅在当前块中有效的局部变量
块: js中只要一个{}就是一个代码块
let声明的变量,仅在{}内有效,不会被提前到{}外
2. 防止声明提前现象
声明提前: 在开始执行程序前,引擎会将var声明的变量和function声明的函数,提前到"当前作用域"顶部集中优先创建,再开始执行程序 /*但是赋值留在原地*/
何时:今后强烈建议用let代替var
强调:let必须配套严格模式使用
_______________________________________________________________________________________________
箭头函数: 对所有回调函数的终极简写
何时: 今后,几乎所有的回调函数,都要用箭头函数简化
如何:
1.所有回调函数都可:去 function(参数) 改为 (参数)=>
2.如果函数体只有一句话: 可省略{}
如果这一句话还是return,可省略return
3.如果只有一个参数:可省略()
但是,如果没有参数,必须保留空()
特点:箭头函数可让内外this共用同一个对象————不再需要bind替换
特殊: 如果不希望内外共用this,就不能用箭头函数(或者用e.target -> 当前单击的元素对象)
比如事件处理函数:
elem.addEventListener("click",function(){ this -> 当前单击的元素对象,不共用 })
elem.addEventListener("click",()=>{ 致使this -> 不是elem })
变通解决:
elem.addEventListener("click",e=>{ e.target->elem }) /*利用冒泡*/
特点:
① 箭头函数没有this
② 箭头函数没有arguments
③ 不能通过 new 关键字调用
④ 没有 new.target
⑤ 没有原型
⑥ 没有 super
_______________________________________________________________________________________________
for of: 简化for循环遍历:
何时: 直接获得每个元素值时
如何:
for(var i=0;i<arr.length;i++){ arr[i] } /*arr[i]: 当前元素*/
简写为:
for(var val of arr){ val } /*val: of会依次取出arr中每个元素的值,保存到val*/
局限:
①只能遍历索引数组和类数组对象,不能遍历关联数组和对象(只能用for in循环遍历)
② 无法获得下标
③ 只能逐个遍历所有,不能控制循环的开始和结束以及步调
④按值传递: 如果数组中保存的是原始类型的值,修改val,不会影响数组元素(val是数组元素值的副本)
何时:仅遍历元素值,不关心下标时,才可用for of简写
_______________________________________________________________________________________________
class: 对 面向对象 的简化
如何定义类型:
①用一个 class 类型名{}结构包裹原来的 构造函数和原型对象方法
②修改构造函数的'function'为'constructor',其余保持不变
③可省略开头的'类型.prototype'与方法名后的'=function'
直接定义在class中的函数直接量,会自动保存在当前类型的原型对象中
定义父类:
class Flyer{
constructor(fname,speed){
this.fname=fname;
this.speed=speed;
}
fly(){ } //fly <=> Flyer.prototype.fly
get 访问器属性名(){ return this.受保护的其他属性 } //添加访问器属性,在构造函数的平级
set 访问器属性名(val){
if(条件) this.受保护的属性=val
else 报错
}
}
继承: 不再设置Object.setPrototypeOf
①在'class 类型名'后添加'extends 父类型'名
② 在子类型构造函数中不允许直接用call调用父类型构造函数,可使用super(属性参数值列表),不加this
子类:
class Plane extends Flyer{ //让Plane继承Flyer
constructor(fname,speed,score){
super(fname,speed); //super:关键字,指父类型的构造函数,自动将子类型的this传入父类构造函数中,不允许用call
this.score=score;
}
getScore(){ }
}
使用:
var obj = new Plane('fname',10,30) //也可以new父类: var obj = new Flyer('fname',10)
obj.getScore() //子类的方法
obj.fly() //父类的方法