JS高程学习笔记(1-7章)

1.1 一个完整的js实现包括啥?
ECMAscript
DOM(文档对象模型)
BOM(浏览器对象模型)
1.2 ECMAScript的宿主环境有?
宿主环境:提供基本的ECMAScript实现,也提供语言的扩展,便于语言和环境之间对接交互。
宿主环境有:Web浏览器,node,flash,webview
1.3 为何要DOM?
确保代码可以跨平台使用,
1.4 DOM各级的区别?
1级映射文档结构
2级事件和支持CSS
3级加载和保存文档的方法
1.5 BOM的作用是?
提供了独立于内容而与浏览器窗口进行交互的对象,提供了访问窗口对象的一些方法。最强大的功能就是提供了一个访问HTML页面的入口—document对象,使我们可以通过这个入口来使用DOM的强大功能,说的简单一点就是让js可以控制页面以外的部分。
2.1 带src属性的script元素与其闭合标签嵌入JS代码有何问题?
只会下载并执行外部脚本文件,function sayHi(){alert(“Hi!”)被忽略,不执行。
在这里插入图片描述

2.2 如何避免包含太多JS代码页面的空白?
js引用放在body页面后面,如例子加粗部分。
在这里插入图片描述

2.3 为何最好一个页面只包含一个延迟脚本?
若出现了两个延迟脚本,HTML5规范要求脚本按照出现的先后顺序执行,因此第一个先于第二个执行,而这两个脚本会先于DOMContentLoaded事件执行,在现实当中,延迟脚本并不一定按照顺序执行,也不一定在DOMContentLoaded事件触发前执行,因此最好只包含一个延迟脚本。
2.4 为何建议异步脚本最好不要在加载期间修改DOM?
因为页面会先于脚本加载页面,容易出问题
2.5 noscript元素中的内容何时出现?
浏览器不支持脚本或者脚本被禁用
3.1 为何要用严格模式?
避免不确定行为和不安全的操作。
在这里插入图片描述

3.2 省略分号有何隐患?
若将分号省略,那么b的值就为4,而不是3了。容易带来错误和降低性能(解析器需要推测)
在这里插入图片描述

3.3 在控制语句中使用代码块的好处?
让编码意图清晰和降低修改代码出错的几率。
在这里插入图片描述

3.4 为何说JS的变量是松散类型的?
松散类型:可以用来保存任何类型的数据。
因为变量仅是保存值的占位符,变量可以看做存储数据的容器。
在这里插入图片描述

3.5 js的数据类型有?
基本数据类型:undefined,null,Number,Boolean,String
引用数据类型Object
3.6 typeof的作用?
检测变量的数据类型

3.7 typeof的返回值有?
undefined,number,boolean,string,object,function
3.8 引入undefined的目的是?
区分空对象指针与未初始化的变量
3.9 对未声明变量只能执行的操作是?
typeof检测其类型
3.10 为何建议显示初始化变量?
便于使用typeof区分变量是未声明还是未初始化
3.11 null值表示什么?
空对象指针
3.12 为何把变量显示初始化为undefined没必要,而把对象初始化为nul却有必要?
体现nul作为空指针的惯例,进一步区分nu和undefined
3.13 Boolean的字面值区分大小写吗?
区分,所以False,与false不一样
3.14 0.1+0.2不等于0.3?
浮点数的精度是17位,存在舍入误差
3.15如何理解NaN?
表示一个本来要返回数值的操作数未返回数值的情况
3.16JS中除0会阻止代码的执行吗?
不会,因为它返回了NaN
3.17对象调用isNaN()函数有何不同?
先调用valueOf()进行判断,视情况再调用toString()进行判断
3.18数值转型函数Number与parselnt/parseFloat有何区别?
前者针对任何类型,后两者只针对字符串
3.19parselnt和parseFloat有何区别?
一是parseFloat不忽略第一个小数点;二是parseFloat始终忽略前导0;三是parseFloat只解析十进制;
3.20怎样避免parselnt错误解析字符串?
任何情况下明确基数。
3.21Number与parselnt解析空字符串时有何区别?
前者返回0,后者返回NaN
3.22转换为字符串的方法及区别?
toString()通过指定基数,改变输出的值,null和undefined没有toString方法()String()将任何类型的值转换成字符串
3.23如何改变数值的toString()方法的返回值?
传入指定基数
3.24为何说Object是所有实例的基础?
Object所具有的任何属性和方法也存在于具体对象中。
3.25递增/减前置和后置的区别?
前置时变量在语句执行前改变,后置则是在语句执行后改变
3.26系统怎样对非数值应用位操作符进行自动处理?
使用Number()函数把该值转换为数值
3.27如何理解短路操作?
第一个数决定结果,则不会操作第二个数
3.28比较数值与字符串时是如何转换的?
字符串转换为数值
3.29任何操作数与NaN的比较结果是?
flase
3.30相等和全等的区别?
”只要求值相等;“=”要求值和类型都相等
全等不需要转换,性能更好
3.31为何NaN不等于NaN?
逻辑上设计如此
3.32 逗号操作符的赋值规则是?
返回表达式最后一项
例:
Var num = {5、1、2、0}; //num的值为0
由于0是表达式中的最后一项,因此num的值就是0。
3.33 为何for…in循环输出的属性名不可预测?
在这里插入图片描述
Js对象属性没有顺序
3.34 为何不建议大量使用with语句?
因为它会去检查大括号内的每个变量是否是这个对象的属性或者方法,使得JavaScript的执行时间增加。性能下降,调试困难
在这里插入图片描述

3.35 为何switch语句比较时不发生转换?
使用的是全等比较(例如:字符串“10”不等于数值10)
3.36 为何位于return语句后的代码不会执行?
函数执行return后会立即停止并退出,所以警示框永远不会被调用。
在这里插入图片描述

3.37 JS函数中通过什么获取参数数组?
arguments对象
用法:
(1)利用argument[i]访问函数参数
在这里插入图片描述
(2) 利用argument返回参数得个数(argument.length)
在这里插入图片描述

3.38 为何JS中的函数不能实现重载?
如例子中声明了两个同名函数,但从结果看,调用的是第二个函数,第一个函数被第二个函数覆盖了。原因在于在创建第二个函数的时候,实际上它覆盖了引用第一个函数的变量add。
无法做到参数类型和数量的不同
在这里插入图片描述
3.39 相等操作符的运用(==)在这里插入图片描述

3.40 toFixed()在一些浏览器采用的策略
因为toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。例如将数据Num保留2位小数,则表示为:toFixed(Num);但是其四舍五入的规则与数学中的规则不同,使用的是银行家舍入规则,银行家舍入:所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。具体规则如下:简单来说就是:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
在这里插入图片描述
3.41 JS的浮点数值舍入会产生浮点误差问题的解决办法
在这里插入图片描述
4.1引用数据类型
应用类型的值是保存在内存中的对象。JavaScript不允许直接访问内存的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。
引用数据类型包括Object类型,Array类型,Date类型,RegExp类型,Function类型
4.2 复制变量值
把一个变量赋值给另外一个变量,这两个变量的内存地址是相互独立的。
当从一个变量向另一个变量复制引用类型的值时,这个变量是一个指针副本,指向这个对象的属性。他们之间是关联的,相互影响的。
在这里插入图片描述
4.3 执行环境
1.执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象里。
2.全局执行环境是最外围的一个执行环境。在web浏览器中,全局执行环境被认为是window对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。全局执行环境直到应用程序退 出——例如关闭网页或浏览器——时才会被销毁。

3.每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。ECMAScript 程序中的执行流 正是由这个方便的机制控制着。
4.4 作用域链
标识符解析是沿着作用域链一级一级的搜索标识符的过程。在这里插入图片描述

4.5 变量提升
在if,for语句中使用var定义的变量会提升到代码的前面;且function比变量提升得更前。如果在局部作用域中未对一个变量进行声明,则会默认将变量定义在全局作用域中
在这里插入图片描述
4.6 垃圾收集
垃圾收集机制的原理:找出那些不再继续使用的变量,然后释放其占用的内存。

JavaScript 具有自动垃圾收集机制,最常用的垃圾收集方式是标记清除,首先,给存储在内存中的所有变量加上标记,然后会去掉环境中的变量以及被变量引用的变量。当变量离开环境时,则将其 标记为“离开环境“,最后,垃圾收集器 完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。该回收机制的缺点是标记过程必须遍历所有的内存来找到垃圾。这种算法的思想是给当前不使用的值加上标记,然后再回收其内存

另一种不太常见的垃圾收集策略叫做引用计数(reference counting)。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。 如果同一个值又被赋给另一个变量,则该值的引用次数加 1。当垃圾收集器下次再运行时,它就会释放那 些引用次数为零的值所占用的内存。但这个方法在指针相互引用(也就是环形引用)方面有严重问题,已被弃用、
4.7 解除引用
优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好将其值设置为null来释放引用——这个做法叫做解除引用
解除一个值的引用并不意味着自动回收该值所占用的内存,解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

5.1 对象定义
对象是某个特定引用类型的实例。新对象是使用new操作符后跟一个构造函数来创建的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。
5.2 instance 使用
在 JavaScript 中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 “object”。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。
instanceof 示例
在这里插入图片描述
instanceof 常规用法在这里插入图片描述

instanceof 在继承中关系中的用法
在这里插入图片描述

5.3 栈
栈是一种 LIFO(Last-In-First-Out, 后进先出)的数据结构,也就是新添加的项早被移除。而栈中项的插入(叫做推入)和移除(叫做 弹出),只发生在一个位置——栈的顶部。ECMAScript为数组专门提供了 push()和 pop()方法,以便 实现类似栈的行为。
5.4 队列
队列数据结构的访问规则是FIFO(First-In-First-Out,先进先出),队列在列表的末端添加项,从列表的前端移除项。shift():从列表的前端移除项;unshift():从列表的前端添加项。
5.5 重排序:reverse()
该方法会反转数组项的顺序,不过该方法不够灵活,所有有了sort()方法
在这里插入图片描述

5.6 重排序:sort()
该方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,确定如何排序。
在这里插入图片描述

可以接收一个比较函数,进行升序和降序操作:
在这里插入图片描述

5.7 concat()方法:添加数据项
可以基于当前数组中的所有项创建一个新数组。该方法会创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
在这里插入图片描述

5.8 slice(start,end)方法:截取数据项
可以基于当前数组中的一个或多个项创建一个新数组。可以接收一或两个参数,即要返回项的起始和结束位置。只有一个参数,表示从指定位置开始到结束的所有项;如果两个参数,返回起始和结束位置之间——但不包含结束位置的项。
在这里插入图片描述

5.9 splice()方法:删除,插入操作
1:删除的功能
splice(index,count)
参数:
index:开始位置的索引
count:要删除元素的个数
返回:返回的是包含被删除元素的数组对象
例如:
在这里插入图片描述

结果:
在这里插入图片描述

2:插入功能
splice(index,0,插入的项)
参数
index:插入元素的索引值
例如:
在这里插入图片描述

5.10 位置方法(indexOf()和lastIndexOf())
这两种方法都接收两个参数:要查找的项和(可选)表示查找起点位置的索引;
在比较第一个参数与数组中的每一项时,会使用全等操作符
indexOf()表示从数组开头开始查找;
lastIndexOf表示从数组的末尾开始查找;
在这里插入图片描述

5.11 迭代方法
every():对数组中的每一项给定函数,如果该函数对每一项都返回true,则返回true
filter():对数组中的每一项给定函数,返回函数会返回true的项组成的数组,大多用于过滤操作
forEach():对数组中的每一项给定函数,这个方法无返回项,相当于for操作
map():对数组中的每一项给定函数,返回每次函数调用的结果组成的数组,大多用于对数组的处理操作
some():对数组中的每一项给定函数,如果该函数的任一项返回true,则返回true
在这里插入图片描述

5.12 归并方法
reduce()和reduceRight()都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()从数组的第一项开始遍历到最后;而reduceRight()从最后一项开始,向前遍历到第一项。
arr.reduce(callback,[initialValue])

reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
callback (执行数组中每个值的函数,包含四个参数)
1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (数组中当前被处理的元素)
3、index (当前元素在数组中的索引)
4、array (调用 reduce 的数组)
initialValue (作为第一次调用 callback 的第一个参数。)
在这里插入图片描述
5.13 Date类型
new Date(Date.parse(“may,25,2017,12:05:00”));可以设置指定的日期,,如果直接将表 示日期的字符串传递给 Date 构造函数,也会在后台调用 Date.parse()。也就是说new Date(“may,25,2017,12:05:00”);也是等价的,
在这里插入图片描述

5.14 RegExp 类型
1.RegExp的主要方法exec();
在这里插入图片描述

2.RegExp构造函数属性在这里插入图片描述
在这里插入图片描述

5.15 Function类型
1.定义函数的三种方法
(1)函数声明:functon sum(sum1,sum2) { } //解析器优先读取函数声明
(2)函数表达式:var sum = function(sum1,sum2) {} //执行到所在代码行才会真正解析执行
(3)Function构造函数:var sum = new Function() //不推荐
2.apply()和call()函数的作用和区别
作用:都是用来扩充作用域。这两个函数通过设定函数所要运行的作用域,但是对象不需要跟方法有任何耦合。
在这里插入图片描述

5.16 基本包装类型
1.在读取模式中访问字符串时,后台都会自动完成下列处理:
(1)创建一个String类型的实例;var s1 = new String()
(2)在实例上调用指定的方法;var s2 = s1.substring(2)
(3)销毁这个实例;s1 = null;
2.Boolean类型
var b1 = new Boolean(true);b1返回的typeof是object。而var b2 = false;返回的是boolean
建议永远不要使用Boolean对象
2.Number类型
toFixed()具有自动舍入的特性:
在这里插入图片描述

toExponential()可用于格式化数值,返回以指数表示法表示的数值的字符串形式:接收一个参数,为输出结果的小数位数
在这里插入图片描述

3.String类型
ECMAScript 5为所有字符串定义了 trim()方法。这个方法会创建一个字符串的副本,删除前置及 后缀的所有空格。
5.17 Math对象
1.舍入方法:
Math.ceil():向上取整
Math.floor():向下取整
Math.round():执行标准舍入
2.random()方法
值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值 )
ECMA中有两种属性类型:数据属性和访问器属性
6.1数据属性
[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特 性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的 这个特性默认值为 true。
[[Enumerable]]:表示能否通过 for-in 循环返回属性。像前面例子中那样直接在对象上定 义的属性,它们的这个特性默认值为 true。
[[Writable]]:表示能否修改属性的值。默认值为true
[[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候, 把新值保存在这个位置。这个特性的默认值为 undefined。

要修改属性默认的特性,必须使用 ECMAScript 5的 Object.defineProperty()方法。这个方法 接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属 性必须是:configurable、enumerable、writable 和 value。设置其中的一或多个值,可以修改 对应的特性值
在这里插入图片描述

6.2访问器属性
[[Configurable]]:表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特 性,或者能否把属性修改为数据属性。这个特性默认值为 true。
[[Enumerable]]:表示能否通过 for-in 循环返回属性。
[[Get]]:在读取属性时调用的函数,默认值为undefined
[[Set]]:在写入属性时调用的函数,默认值为undefined
在这里插入图片描述

6.3定义多个属性
Object.defineProperties() 方法可以通过描述符一次定义多个属性。这个方法接收两个对象参数,第一个是目标对象,第二个是要添加或修改的属性。
例子:
在这里插入图片描述

6.4 读取属性的特征
使用 ECMAScript 5的 Object.getOwnPropertyDescriptor()方法,可以取得给定属性的描述符
这个方法接收两个参数:属性所在的对象和要读取其描述符的属性名称
在这里插入图片描述

6.5 工厂模式
该模式抽象了创建具体对象的过程,因为 ES 没有类,因此使用一种函数来封装以特定的接口来创建对象。
在这里插入图片描述

6.6 构造函数模式
构造函数始终要以一个大写字母开头,非构造函数应该以一个小写字母开头
在这里插入图片描述

与工厂模式相比自定义的构造函数:
没有显示的创建对象;
直接将属性和方法赋给了 this 对象;
没有 return;
按照惯例构造函数名开头字母是大写。
通过 new 操作符创建 Person 的实例有以下几个步骤:
创建一个新对象;
将构造函数的作用域赋给新对象(因此 this就指向了这个新对象);
执行构造函数中的代码(为新对象添加属性);
新对象复制给一个变量。
6.7构造函数模式的问题
构造函数中的方法在每个实例上都要重新创建一遍,上面例子中的 person1 和 person2 都有一个名为 sayName() 的方法,都不是同一个 Funtion 的实例。会导致不同的作用域和标识符解析。不同实例上的同名函数不相等:
alert(person1.sayName == person2.sayName); // false
6.8 原型模式
我们创建的每一个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象包含可以由特定类型的所有实例共享的属性和方法。使用原型对象可以让所有的对象实例共享原型对象所包含的属性和方法。就是说创建实例的时候构造函数不用传入参数:
在这里插入图片描述

6.9 理解原型对象
几条规则:
创建一个新函数,会为该函数创建一个 prototype 属性,该属性指向函数的原型对象。
默认条件下,函数的原型对象会自动获得一个 constructor 属性,该属性指向 prototype 属性所在的函数。
构造函数的原型对象默认只有一个 constructor 属性,其他的属性或方法都是从 Object 继承来的。
调用构造函数创建的实例,内部包含一个指针指向这个实例的构造函数的原型对象,ES5 中管这个指针叫[[Prototype]],在各个浏览器中该指针实现为__proto__属性。
6.10 原型与 in 操作符
in 的用法:1.在for in 中遍历。2. in 操作符可以判断对象能不能访问到某个属性(不管是在实例还是原型中),例子:
在这里插入图片描述

6.11更简单的原型语法
用包含属性和方法的对象字面量来重写原型对象,例子:
在这里插入图片描述

之前的方法时创建 Person 这个函数后,会自动创建他的 prototype 对象,同时这个原型对象有一个 constructor 属性指回 Person 函数,然后我们在这个原型对象里面添加属性。但是这个新的方法是用一个新对象完全重写了创建函数时自动生成的原型对象,因此 constructor 属性指向了 Object 构造函数,不再指回 Person 函数。因此 constructor 属性不能再用来确定类型了。如果要避免这种情况, constructor 赋为 Person。也可以使用 Object.defineProperty() 方法来重设 constructor 属性。
6.12原型的动态性
由于访问一个对象的属性本质是原型链上的搜索,因此只要在访问实例属性之前修改原型链,都可以在访问实例属性的时候体现出来。
尽管像上面说的可以随时修改原型对象的属性和方法,但是如果是重写整个原型对象,那情况就不一样了。由于原型对象被重写,实例上的[[Prototype]](也就是__proto__属性,它是指向原型对象的)指向的原型对象还是老的那个,访问人为重写的原型对象的属性和方法会报错,例子:
在这里插入图片描述

6.13原型对象的问题
原型模式省略了构造函数初始化的环节,所有的实例默认的属性值都是相同的。
最大的问题如果原型对象中有引用类型的属性,例子:
在这里插入图片描述

*6.14 组合使用构造函数模式和原型模式
是创建自定义类型最常见的方式。构造函数模式用来定义实例属性和传递参数初始化,原型模式用来定义方法和共享的属性,重写之前代码的例子:
在这里插入图片描述

6.15 原型链
回忆原型模式,如果让一个原型对象 A 等于另一个类型的实例 B 呢,那这个原型对象 A 就包含一个指针指向实例 B 的原型对象。如此层层递进,例子:
在这里插入图片描述

例子中 SubType 继承了 SuperType,用 SuperType 的实例重写了 SubType 的原型对象。因为实例是可以指向它的原型对象的,因此,例子中的是实例 instance 指向 SubType 的原型(也就是 SuperType 的实例),SubType 的原型(也就是 SuperType 的实例)又指向了 SuperType 的原型。
getSuperValue 方法在 SuperType 的原型中;superValue 在 SubType 的原型中(因为 superValue 是实例属性,而 SuperType 的实例重写了 SubType 的原型)。调用 instance.getSubValue() 的过程是:1.搜索实例;2.搜索 SubType.prototype;3.搜索 SuperType.prototype。
6.16 确定原型和实例的关系
两种方式确认:
instanceof 操作符:
在这里插入图片描述

由于原型链,instance 都可以看作是 Object,SuperType,SubType 三者的实例。
2.isPrototypeOf() 方法:
在这里插入图片描述

6.17 原型链的问题
上文中的组合使用构造函数和原型来生成新的类型的方法就是为了解决是引用类型的原型属性会被所有的实例共享的问题。原型链继承同样会有这个问题,例子:
在这里插入图片描述

colors 数组是定义在构造函数中的,因此所有的 SuperType 实例都会有自己的 colors 数组。因为 SubType 的原型是 SuperType 的实例,因此 SubType 的原型也有一个 colors 数组,但是这就是导致了上文中讨论的问题,原型中的引用类型会被所有的实例共享,在例子中也就是被所有的 SubType 实例共享。
6.18借用构造函数模式
为了解决原型中包含引用类型带来的继承问题,采用 constructor stealing 的技术。在子类型的构造函数内部调用超类型的构造函数,例子:
在这里插入图片描述

这样每一个 SubType 实例都有自己的 colors 副本了。
6.19 *组合继承
组合继承,也叫伪经典继承。是用原型链和借用构造函数模式组合到一起使用。思路是使用原型链实现对原型属性和方法的继承,用借用构造函数来实现对实例属性的继承,满足了复用性和每个实例都有自己的属性,例子:
在这里插入图片描述

在例子中:
超类的构造函数定义了两个属性,超类的原型对象定义了一个方法。
子类的构造函数借用了超类的构造函数并传入了参数,定义了自己的属性。
用超类的实例重写子类的原型对象,在子类新原型上指定 constructor 属性为子类的构造对象,并在子类新原型上添加了自己的方法。
这样使得不同的实例有自己的属性,使用相同的方法。这是 JavaScript 中最常见的继承模式。
6.20原型式继承
道格拉斯·克罗克福德于2006年提出的这种继承模式。借助原型基于已有的对象创建新的对象,不创建新的类型。
在这里插入图片描述

例子:
在这里插入图片描述
原型式继承,必须有一个对象作为另一个对象的基础,基于 object 函数返回的对象根据自己的需要进行修改。但很明显引用属性会被公用。
ES5 通过新增的 Object.create() 方法规范了原型式继承,create 方法接收两个参数。一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象,(一个参数)只有作为原型的对象参数的例子:
在这里插入图片描述

create 方法的第二个参数与 Object.defineProperties() 方法的第二个参数格式相同,第二个参数中指定的属性会覆盖原型对象上的同名属性,两个参数的例子:
在这里插入图片描述

6.21寄生式继承
寄生式继承也是道格拉斯·克罗克福德提出的,创建一个仅用于封装继承过程的函数,在函数内部增强对象,例子:
在这里插入图片描述

createAnother 函数接收了一个参数作为新对象的基础,将这个参数传递给了上文中的 object 函数。将返回的值赋给 clone,新对象添加新方法,最后返回新对象,使用例子:
在这里插入图片描述

寄生式继承的缺点是函数不能复用。
6.22 *寄生组合式继承
组合继承是最常用的模式,但是也有缺点,会调用两次超类的构造函数:
一次用超类的实例(调用超类的构造函数)重写子类的原型时。
一次是子类型的构造函数内部借用构造函数(借用超类的构造函数)时。
复习组合继承的例子:
在这里插入图片描述

两次调用超类的构造函数,子类都会继承超类的所有实例属性,第二次调用会用新的继承自超类实例属性屏蔽第一次调用时继承的。
通过寄生组合式继承来解决这个问题,通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。思路是用寄生式继承来代替“用超类的实例来重写子类的原型”的过程,例子:
在这里插入图片描述

用这个 inheritPrototype 函数来代替上文例子中的“用超类的实例来重写子类的原型”过程(叫寄生组合式继承模式):
在这里插入图片描述

寄生组合式继承模式高效率在于只调用了一次 SuperType 构造函数,避免创建不必要的属性,同时保持原型链不变,还能正常使用 instanceof 操作符和 isPrototypeOf() 方法。被认为是引用类型最理想的继承范式。

7.1 使用函数实现递归
递归函数是一个函数调用自身的构成的,一个经典的递归阶乘函数,但是会报错!,例子:
在这里插入图片描述

arguments.callee 是一个指向正在执行的函数的指针,用它来实现函数的递归调用,例子:
在这里插入图片描述

警告:在严格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。当一个函数必须调用自身的时候, 避免使用 arguments.callee(),要么给函数表达式一个名字,要么使用一个函数声明。
在这里插入图片描述

这种方式在严格模式和非严格模式下都行得通。
7.2 使用闭包定义私有变量
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式是在一个函数中创建另一个函数,例子:
在这里插入图片描述

内部函数之所以能访问外部函数的参数,是因为当一个函数被调用时会创建一个执行环境和相应的作用域链,作用域链的起点是被调用函数的活动对象(arguments 和命名参数),外部函数的活动对象在第二位,再外部函数的活动对象在第三位,直到作用域链的终点(全局执行环境)。作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
一般来说函数执行完毕,局部活动对象会被销毁,内存只保留全局作用域。但是闭包不同,例子中匿名函数的作用域链为匿名函数的活动对象—> createComparisonFunction 函数的活动对象—> 全局执行环境。因此匿名函数可以访问 createComparisonFunction 函数的 propertyName 参数,而且在匿名函数被赋给变量 compareNames 后(此时 createComparisonFunction 函数已经执行完了)createComparisonFunction 函数的活动对象不会被销毁,因为它还在匿名函数的作用域中。如例子所示,销毁对匿名函数的引用,createComparisonFunction 函数的活动对象才会被销毁。
7.3闭包与变量
经典的闭包例子:
在这里插入图片描述

次数为3次的遍历中的每一个匿名函数都作用域链的第二位都是 createFunctions() 的活动对象,引用的是同一个变量 i,程序运行结束的时候 i 是3,所以输出的结果都是3。
在这里插入图片描述

每一个匿名函数的作用域链的第二位是有着参数为 num 的立即执行的匿名函数的活动对象,num 参数是由处于作用域链第三位的 createFunctions() 的 i 值传递传递进来的,因此最后输出的是 0 1 2。
7.4 关于 this 对象
this 对象是运行时基于函数的执行环境绑定的;
在全局环境中 this 等于 window,当函数作为某个对象的方法被调用时,this 等于这个对象;
匿名函数的执行环境有全局性,this 等于 window。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值