第一章
错误处理:
错误: 程序运行过程中,导致程序无法正常执行的现象(即bug)
现象: 程序一旦出错,默认会报错,并强行退出
错误处理: 即使程序出错,也保证程序不会强行退出的一种机制
为什么: 避免程序强行中断,导致极差的用户体验
何时: 只要希望即使程序发生错误,也保证不强行退出
如何(用于调用函数时):
try{
可能出错的语句(执行语句)
}catch(err){ /*只有发生错误时,才执行*/
错误处理代码: 1.保存数据;2. 提示用户;3. 记录日志 /*console.log(String(err))*/
}
其中:err是错误对象: 错误发生时,自动创建的,记录错误信息的对象
性能问题: 1. 放入try catch中的代码,即使不出错,效率也比正常代码略低
2. 程序出错都会创建错误对象(error),占用更多内存
解决: 如果可提前预知错误的原因,建议用if...else...(没有err)来代替try catch
优点: 1. 不影响程序执行效率;2. 节约内存
主动抛出错误:
什么是: 在本来程序不出错的情况下,为了以防万一,主动新建一个错误,并抛出
何时: 在协作开发中,函数的定义者向函数的使用者抛出错误,用以告知对方错误的使用了函数
如何:throw new Error("错误提示");/*放在 函数/catch 中*/
笔试:js中错误的类型: 6种
SyntaxError:语法错误
ReferenceError:引用错误: 要找的变量不存在
TypeError:类型错误: 错误的调用了API或错误的使用了()或[]
RangeError:范围错误: 参数的取值超范围
URIError,EvalError;
Uncaught SyntaxError: Unexpected identifier
ReferenceError: 变量名 is not defined //可能是因为在严格模式下,没有声明变量
Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
/*块范围声明(let、const、function、class)还不支持外部严格模式,必须启用严格模式*/
var,let,const的区别:
var 定义的变量可以修改,如果不初始化会输出undefined,不会报错
let 制造一个'块级作用域'(if/else/for/while/单独一个{},必须写{})(JavaScript中没有块级作用域)
在 {} 中用let定义的变量仅能在块内使用,对函数外部无影响
const 定义的变量值无法修改,而且必须初始化,范围限于声明它的块中,IE11及以上才支持
(非)严格模式:输出从未声明的变量时会返回: ReferenceError//没有找到对象,在全局输出局部变量时
函数必须调用,才会改变函数外的变量。注意局部变量和全局变量(调用全局变量时,改变的是全局变量)、原始类型和引用类型
函数(不论全局还是局部)若无return a;则返回undefined,即函数的值为undefined。有return a,函数(一般要接住)才有值(a也能是函数)
return的作用: 返回函数外,返回函数的值
如果一个函数没有用new,也没有用.调,它的this只能指向window
_______________________________________________________________________________________________
1.Function创建函数: 3种:
定义好的函数只要加(),就会调用,如 y=fn();
① 声明:
function 函数名(参数列表){函数体;return 返回值}
特点: 会被声明提前(没有赋值,函数体提前)
② 直接量:
var 函数名=function(参数列表){函数体;return 返回值}
特点: 不会被声明提前(声明提前,但赋值不会提前)
揭示:函数名其实是变量,函数定义是一个引用类型的对象,函数名通过函数对象的地址引用着函数
③ 用new(用的很少):
var 函数名=new Function("参数1", "参数2", ..., "函数体;return 返回值")
强调: 无论是参数名还是函数体,都必须放在""中,变量放在外面
2.重(chong)载(overload):
什么是: 相同函数名,不同参数列表的多个函数,在调用时,可根据参数的不同,自动选择对应的函数执行
为什么: 减少API的数量,减轻调用者的负担
何时: 一项任务,如果根据不同的参数,执行不同的操作流程时
问题:js默认不允许多个同名函数同时存在,即不支持重载语法
后果: 最后一个函数会覆盖之前所有函数
解决(3步):
① 仅定义一个函数,不要写参数列表
②arguments: 函数内自动创建的,接收所有传入函数的参数值的类数组对象/*argument: 参数*/
函数名.length表示函数定义时参数的个数(可写在函数外),arguments.length指外部调用时传参的个数
类数组对象: 长的像数组的对象
VS 数组: 相同:1. 下标, 2. .length, 3.for遍历
不同: 类型不同 ——— API不通用
③ 函数内,根据arguments中参数的个数或类型,动态决定执行不同操作
if(arguments.length==n)...;else /*参数的.length(个数)为n时,执行if后的语句*/
问题: 是否还需要参数
答:需要: 1. 参数用于提示调用者如何正确使用函数
2. 参数名一般都比arguments简洁且见名知义
3.匿名函数:
匿名函数,若不改变this的指向(call、apply、bind),则this都指向window
什么是:定义函数时,没有指定函数名的函数
何时:2种: 只用一次的函数,都不要写名
为什么:
①节约内存,及时释放:
垃圾回收: 垃圾回收器程序会自动释放不被任何变量引用的对象
垃圾回收器: 专门检查并回收不再被使用的对象的小程序
②划分临时作用域,避免产生全局变量污染
如何: 2种
① 回调: 自己创建函数后,不调用,传递给其它函数,让其它函数使用
比如: arr.sort(function(a,b){ return a-b })
str.replace(reg,function(kw){return 替换值})
btn.addEventListener("事件名",function(){...})
注:回调函数可以单独拿出来定义,在调用时不要加括号
② 自调: 定义函数后,立刻调用自己,调用后立刻释放
如何:(function(){...})()
包围函数的一对括号将其转换为一个表达式,紧跟其后的一对括号调用了这个函数
可以不加一元运算符,但前面的js语句需加分号(;)
若前面刚好有个函数没有以;结尾,那么在匿名函数自调时会发生错误(可在前面加个;)
+function(){...}() /*不常用,+易与运算符混淆*/
其它: 在匿名函数附近使用括号或一些一元运算符,用来引导解析器
何时:不希望产生任何全局变量污染时,都可将代码封装在匿名函数中,自调执行
优点: 划分临时作用域,避免创建全局变量
_______________________________________________________________________________________________
4.作用域(scope)和作用域链(scope chain)
什么是作用域: 变量的使用范围,也是一个变量的实际存储位置
包括2种作用域:
1. 全局作用域: window
保存: 全局变量
优点: 随处可用,可重复使用
缺点: 易造成全局污染
2. 函数作用域(AO):
保存: 局部变量
优点: 仅函数内可用,不会被污染
缺点: 不可重用
函数的生命周期:
① 程序开始执行前: 调用浏览器主程序,创建全局作用域对象window
② 定义函数:
在全局创建函数名变量
在window外创建函数对象保存函数定义
函数名变量使用地址引用函数对象
函数对象的隐藏属性scope指回函数诞生的作用域window
③ 调用函数:
在ECS中添加本次函数调用的记录
创建本地函数调用所需的函数作用域对象AO(活动对象)
在AO中创建局部变量(var 声明的和参数变量)
AO的隐藏属性parent指向父级作用域对象
变量使用顺序: 优先使用函数作用域AO中的局部变量
局部没有,才去父级作用域找
④ 调用后:
ECS中的本函数调用的记录出栈
导致函数作用域AO没人引用,被释放
导致AO中的局部变量一起释放
作用域链(scope chain):由多级作用域对象,逐级引用形成的链式结构
2个职责: 1. 保存所有变量: 全局变量都保存在window中,局部变量都保存在AO中
2. 控制变量的使用顺序: 先局部,再全局
_______________________________________________________________________________________________
5.闭包(closure):
什么是: 既重用变量,又保护变量不被污染的一种机制
为什么: 全局变量和局部变量都有优缺点:
全局变量: 优: 可反复使用,随处可用
缺: 易被污染
局部变量: 优: 仅函数内可用,不会被污染
缺: 不可重用
何时:只要希望既重用变量,又保护变量不被污染,就只能用闭包
如何: 3步:
① 外层函数,包裹受保护的变量和操作变量的内层函数
每次执行函数都将受保护的变量的值保存在闭包中(这个变量是局部变量)
② 外层函数将内层函数返回 /*return function(){...}*/
③ 使用者调用外层函数,获得返回的内层函数对象
笔试:闭包如何形成: 外层函数的函数作用域对象(AO)无法释放
缺点: 比普通函数占用更多内存空间———多的是外层函数的作用域对象AO
解决: 如果闭包不再使用,就要及时释放
将引用内层函数的变量赋值为null,导致内层函数对象先释放,使外层函数的AO一并释放
笔试(画简图):2步:
1.先找受保护的局部变量(避免被污染),并确定最终保存的值
外层函数的局部变量(var的或参数变量),受保护: 不会被全局的操作修改
就算局部变量循环,也只会有一个最终的值
2.再找操作变量的内层函数对象:3种情况:
① 直接return function
② 给全局变量赋值:全局变量=function
③ 将内层函数保存在数组/对象内,整体返回
特点:
外层函数声明局部变量并返回内层函数
内层函数操控(使用)局部变量
全局变量引用外层函数
一次外层函数调用,返回的多个内层函数,共用同一个受保护的变量
第二章*****************************************************************************************
1.面向对象OOP
事物的属性会成为对象中的属性: 属性就是保存在对象中的变量,用法和普通变量完全一样
事物的功能会成为对象中的方法: 方法就是保存在对象中的函数,用法和普通函数完全一样
属性和方法统称为对象的成员
定义属性、定义方法{}用逗号,结尾 创建新对象、调用方法用分号;结尾
方法和函数的唯一不同之处: 方法始终与特定对象关联并与其配合使用
delete Obj[key]//删除对象成员key,无法删除变量、函数(无法删除数组元素,会变为undefined)
'name' in obj //判断对象中是否拥有某个属性(包括继承属性),返回Boolean
obj.hasOwnProperty("a") //判断对象是否有某个自有属性(不包括继承属性)
什么是面向对象编程: 程序中都是用对象结构来描述现实中一个具体事物
对象: 内存中集中存储一个事物的属性和功能的程序结构
为什么: 便于维护和使用数据
何时: 今后只要维护的数据量或种类多时,都要用对象的方式,集中管理每个事物的属性和功能
如何:三大特点: 封装,继承,多态
封装:创建一个对象
什么是封装: 将一个事物的属性和功能集中保存在一个对象中,再起一个统一的名字
为什么封装: 让每个数据都有其专门的归属,便于维护和查找事物的属性和功能
何时封装: 只要使用面向对象,都要先将事物的属性和功能集中定义在在对象中,再使用
如何封装: 3种:
只创建一个单独的对象: 2种
1.对象直接量:
var obj={/*创建一个新对象*/
属性名:属性值,
... : ...,
方法名:function(参数列表){ }, //旧写法
方法名(参数列表){this.属性名...}, //ES6新写法
方法名(...){...}
}
何时使用直接量: 如果创建对象时就已经知道对象的成员
对象自己的方法如何访问对象自己的属性:
错误: 对象名.属性名, 一旦对象名修改,方法内的对象名要同时修改,不便于维护(但语法无误)
正确: 在方法内使用关键词this自动指代当前对象本身: this.属性名
this: 能够自动获得正在调用的 当前函数.前 的对象的关键词
为什么: 不带this的普通变量,无法进入当前对象内查找属性,默认在作用域链中找
何时:只要对象的方法,想访问自己的属性,都必须加this
this可翻译为: 当前对象的属性 .读作的
如何访问对象的成员:
如何访问对象的属性: 对象.属性名或对象["属性名"]
如何调用对象的方法: 对象.方法名()或对象["方法名()"]
2.用new:2步:
①先创建空对象: var obj=new Object(); //可以不加括号/new,也可把Object换成其它构造函数
②向空对象中添加新属性和方法:
obj.属性名=值;//修改: 对象.自有属性名=新值
obj.方法名=function(){...this.属性名...}
何时使用: 创建对象时,暂时不知道对象中的成员
强调:js中的对象创建后,可在任何时候,添加任何新属性和方法
如何: 只要尝试给不存在的属性和方法赋值,对象会自动创建该属性
js中对象的本质: 对象其实就是关联数组,只不过用法更简单
VS 关联数组:① 下标: obj["成员名"] <=> obj.成员名(若成员名是变量,可写为obj[变量])
② 都可随时添加新属性
③遍历: for(var key in obj){ obj[key] }
3.用构造函数(constructor)反复创建多个相同结构的对象:
构造函数: 描述一类对象统一结构的函数,还用于将一个新的空对象装修成想要的结构并存入数据
何时: 只要反复创建多个相同结构的对象时,都要先用构造函数定义一类对象的统一结构
如何:2步:
①定义构造函数: 创建函数的3种方法都可以定义构造函数(在函数内定义属性和方法要用this)
function 类型名(属性参数列表){ /*在构造函数中,所有的属性名和方法名前都要加this.*/
this.属性名=属性参数; /*从属性参数列表 传值给属性名*/
... = ...; /*this指new的新对象*/
this.方法名=function(){ //需要返回的方法不要传参数,参数从this.*获取
...this.属性名...
} }
②用new调用构造函数创建新对象:
var obj=new 类型名(属性值列表); //创建"类型名"这种类型的对象 (同时也会执行这个构造函数)
()前的都是'类型名',()后的.后面的是'类型名'的成员
//定义好的函数只要加(),就会调用,如 y=fn();
//所有函数都可以用 new 实例化
new做了4件事:
1. 创建一个新的空对象
2. 让新的空对象继承构造函数的原型对象
3. 调用构造函数
① 设置构造函数中的this指向新的空对象
② 通过强行赋值的方式,为新对象添加新属性和方法
4. 返回新对象的地址保存在变量中
总结: 构造函数担当着两个角色
1. 图纸: 描述一类对象的统一结构
2. 装修队: 在空对象中添加固定的属性和方法
问题: 方法定义在构造函数内,每个新对象实例,都重复创建了一个完全相同的函数对象 ——— 浪费内存
总结: 构造函数: 优点: 代码重用 缺点: 无法节约内存
解决: 继承
_______________________________________________________________________________________________
继承:
什么是: 父对象的成员,子对象无需重复创建就可直接使用(子对象继承父对象的共有成员)
为什么: 代码重用,节约内存
何时: 如果多个子对象,拥有相同的属性值或方法定义时,只需要将相同的属性和方法定义集中定义在父元素中一份即可,所有子对象共用
如何: js中的继承都是继承原型对象
原型对象(prototype): 集中存储同一类型的子对象'共有属性和方法'的父对象
如何获得原型对象:
买一赠一: 创建构造函数同时,已经自动创建了该类型的原型对象。构造函数的prototype属性引用着所有子对象的父对象(即原型对象)
如何向原型对象中添加共有属性和方法:
构造函数.prototype.属性名=值
构造函数.prototype.方法名=function(){...}
强调: 原型对象中的方法,要想访问对象自己的属性,也必须加this.
总结:
每个子对象,值不同的属性,都要定义在构造函数中
所有子对象共有的相同方法和属性值,都要集中定义在原型对象中
共有属性和自有属性: 自有属性: 直接保存在对象本地的属性
共有属性: 保存在原型对象中,所有子对象共用的属性
相同: 获取属性值:对象.属性名
不同: 修改属性值:自有属性可直接通过子对象修改:子对象.自有属性名=值
共有属性只能通过构造函数的原型对象:构造函数.prototype.共有属性名=值
任何对象都有__proto__属性指向其父对象
任何函数都有prototype属性指向其原型对象
原型链:由多级父对象逐级继承形成的链式结构
保存着: 所有对象的属性和方法
控制着: 对象成员的使用顺序: 优先在对象本地找自有属性使用,自己没有才沿原型链向父对象查找
VS 作用域链: 由多级作用域对象逐级引用形成的链式结构
保存着: 所有的变量
控制着: 变量的使用顺序: 优先在函数作用域对象AO中查找使用,局部没有,才沿作用域链向父级作用域找
凡是不加.的变量,都在作用域链中找
凡是用.才能访问的属性,都在原型链中找
内置对象的原型对象:
每种内置类型都有一对儿构造函数和原型对象:
创建对象: 自动调用内置对象的构造函数
比如: var arr=new Array();
var now=new Date();
var reg=new RegExp();
原型对象: 负责集中存储该类型可用的所有API
比如: Array.prototype中的: .sort() .push() .slice()
因为: Array.prototype:{
sort(){...},
push(){...},
slice(){...},
...
}
_______________________________________________________________________________________________
11个内置对象除Window(NodeJS中的Global)和Math(这两个对象属于对象类型,不是函数类型)均为构造函数
Number、String、Boolean、Function、Array、Date、RegExp、Error、Object
所有的构造函数均继承于Function.prototype(手动创建的构造函数一样,与内置对象在原型链上平级)
Function.prototype继承于Object.prototype(Window、Math、手动创建的对象也继承于Object.prototype。虽然Object.prototype是所有构造函数的父级对象,但Object仍然是函数)
所有的原型对象均继承于父级原型对象,最终继承于Object.prototype
所以可以强行调用: Object.prototype的toString方法来判断类型:
console.log(Object.prototype.toString.call(类型名))
修改继承的方式:
① call、apply、bind
② child.__proto__=father,不推荐,要使用 Object.setPrototypeOf(child, father)
③ 构造函数.prototype.属性名=值 构造函数.prototype.方法名=function(){...}
深度复制对象(引用不同的地址,互不影响)
newObj = JSON.parse(JSON.stringify(sourceObj)) //不能复制对象中的方法
newObj = Object.assign({}, obj1, obj2) //浅拷贝(对象中的对象是直接引用的)
Object.create(null); //创建一个没有任何成员,且没有父对象的空对象
JSON.parse(obj, (key, value) => { return value }) //第二个参数可以在返回之前转换属性的值
JSON.stringify(obj, null, 2); //将对象格式化JSON输出
第一个参数: ① 是函数时,可以改变对象序列化中的行为
② 是包含String和Number的数组,可作为白名单,对对象过滤后再生成字符串
③ 是null或被省略,则所有的键值都被包含在生成的JSON字符串
第二个参数: ① 若是Number(1-10),表示空格的数量
② 是String,则空格会替换为该字符串
③ 是null或被省略,不会显示空白符
_______________________________________________________________________________________________
解决浏览器兼容性问题: 旧浏览器无法使用新API
2步:
1. 判断当前类型的原型对象中是否包含想用的API
比如: if(!"indexOf" in Array.prototype)
其中in用于检查左边的成员名是否在右边的对象中或对象的原型链上
if(typeof 类型.prototype.API!=="function")
比如: if(typeof Array.prototype.indexOf!=="function")
2. 如果没有,就向当前类型的原型对象中添加一个自定义的同名API
类型.prototype.API=function(){
this./*this: 代表当前类型(比如数组)对象*/
}
一、JavaScript继承是类继承还是原型继承
是原型继承,尽管使用了new关键字,但只是一个语法糖,与类无关。JavaScript中没有类
二、prototype用来做什么
prototype是个对象,只有函数有,用来存储对象的成员(属性和方法)的地方,是实现JavaScript原型继承的基础
三、__proto__用来做什么
__proto__是个指向prototype的引用,用以辅助原型继承中向上查找的实现,是一个指向[[Prototype]]的引用
四、constructor用来做什么
是对象上一个指向构造函数的引用,用来辅助instanceof等关键字的实现
_______________________________________________________________________________________________
多态:同一个函数在不同情况下,表现出不同的状态
包括:重载(overload)和重写(override)
重写(override): 如果子对象觉得从父对象继承的成员不好用,可在子对象本地定义同名自有成员,覆盖父对象的成员
为什么: 父对象继承来的成员,不一定都是想要的
何时: 只要父对象不是想要的,就可以修改继承关系,继承另一个对象
如何: 3种:
1.仅修改一个对象的__proto__属性指向新父对象:
child.__proto__=father
问题: __proto__是内部属性,不推荐使用
解决: 使用Object.setPrototypeOf(child,father)
2.通过修改构造函数的原型对象,批量修改所有子对象的父对象:
构造函数.prototype=father
时机: 在开始创建子对象之前,就要换
3.两种类型间的继承:
何时: 如果多个类型之间存在部分相同的属性结构和方法定义时,就可抽象出一个公共的父类型
如何:
①抽象出公共父类型:
父类型的构造函数中定义相同的属性结构
父类型的原型对象中定义相同的方法
②让子类型继承父类型:
让子类型的原型对象继承父类型的原型对象
Object.setPrototypeOf(子类型.prototype,父类型.prototype);
③在子类型的构造函数中借用父类型的构造函数
错误: 直接调用: 父类型构造函数(参数值...)
原因: 直接调用函数时,函数中的this默认指向window,而不是当前新对象
正确: 用call强行调用,并替换this为指定对象:
父类型构造函数.call((正确的)this,其它参数...);
call:专门用于强行替换函数内不正确的this为想要的对象
何时: 只要函数中的this不是想要的,就可用call换成任意对象
比如: fun.call(obj,参数值...) 调用fun,替换fun中的this为obj
第三章*****************************************************************************************
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() //父类的方法