JS高程读书笔记–第六章
明白对象
建立自定义对象的体式格局有建立一个Object实例,然后为它增加属性和要领。还可用建立对象字面量的体式格局
属性范例
ECMAScript在定义只需内部采纳的特性时,形貌了属性的种种特性。
ECMAScript中有两种属性:数据属性和接见器属性。在JS中不能直接接见它们。
数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。
[[Configurable]]
:示意可否经由历程delete删除属性从而从新定义属性,可否修正属性的特性,或许可否把属性修正为接见器属性。默许值为true;[[Enumerable]]
:示意可否经由历程for-in轮回返回属性。默许值为true;[[Writable]]
:示意可否修正属性的值。默许值为true;[[Value]]
:包含这个属性的数据值。读取属性值的时刻,从这个位置读;写入属性值的时刻,把新值保留在这个位置。默许值为undefined;
要修正属性默许的特性,必需运用ES5的Object.defineProperty()
要领。这个要领吸收三个参数:属性地点的对象、属性的名字和一个形貌符对象。在挪用这个要领时,假如不指定,configurable、enumerable和writable特性的默许值都是false;
一旦把属性定义为不可设置的,就不能再把它变回可设置的。
接见器属性
接见器属性不包含数据值;他们包含一对getter和setter函数(这两个函数都不是必需的)。在读取接见器属性时,会挪用getter函数,这个函数担任返回有效值;在写入接见器属性时,会挪用setter函数并传入新值,这个函数担任决议如何处置惩罚数据。
[[Configurable]]
:示意可否经由历程delete删除属性从而从新定义属性,可否修正属性的特性,或许可否把属性修正为数据属性。默许值为true;[[Enumerable]]
:示意可否经由历程for-in轮回返回属性。默许值为true;[[Get]]
:在读取属性时挪用的函数。默许值为undefined;[[Set]]
:在写入属性时挪用的函数。默许值为undefined;
接见器属性不能直接定义,必需运用
Object.defineProperty()
来定义。
不一定非要同时指定getter和setter。只指定getter意味着属性是不能写,尝试写入属性会被疏忽。在严厉情势下,尝试写入只指定了getter函数的属性会抛出毛病。类似的,只指定setter函数的属性也不能读,否则在非严厉情势下会返回undefined,而在严厉情势下会抛出毛病。
定义多个属性
可以应用ES5定义的Object.defineProperties()
要领经由历程形貌符一次定义多个属性。这个要领吸收两个对象参数:第一个对象是要增加和修正其属性的对象,第二个对象的属性与第一个对象中要增加或修正的属性一一对应。
读取属性的特性
运用ES5的Object.getOwnPropertyDescriptor()
要领,可以取得给定属性的形貌符。这个要领吸收两个参数:属性地点对象和要读取其形貌符的属性称号,返回值是一个对象。
建立对象
工场情势
这类情势笼统了建立详细对象的历程。开发人员发清楚明了一种函数,用函数来封装以特定接口建立对象的细节。
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var person1 = createPerson('junyan','25','FE');
工场情势虽然处理了建立多个类似对象的题目,但却没有处理对象辨认的题目(即如何晓得一个对象的范例)
组织函数情势
可以建立自定义的组织函数,从而定义自定义对象范例的属性和要领。
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
};
}
var person1 = new Person('junyan','25','FE')
这类体式格局与工场情势存在以下差别:
没有显式的建立对象;
直接将属性和要领赋给了this对象;
没有return语句;
要建立Person新实例,必需运用new操作符。以这类体式格局挪用组织函数现实上会阅历以下4个步骤:
建立一个新对象;
将组织函数的作用域赋给新对象(因而this就指向了这个新对象);
实行组织函数中的代码(为这个新对象增加属性);
返回新对象;
实例都有一个constructor
(组织函数)属性,该属性指向Person。对象的这个属性最初是用来标识对象范例的。然则,提到检测对象范例,还是instanceof
操作符要更牢靠一些。
建立自定义的组织函数意味着未来可以将它的实例标识为一种特定的范例
将组织函数当作函数
组织函数与其他函数的唯一区分,就在于挪用它们的体式格局差别。任何函数,只需经由历程new操作符来挪用,那它就可以作为组织函数;
不运用new操作符挪用,属性和要领都被增加给window对象
组织函数的题目
运用组织函数的主要题目就是每一个要领都要在每一个实例上从新建立一遍。
差别实例上的同名函数是不相等的,可以经由历程把函数定义转移到组织函数外部来处理。
原型情势!!
我们建立的每一个函数都有一个prototype
(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用处是包含可以由特定范例的一切实例同享的属性和要领
prototype就是经由历程挪用组织函数而建立的谁人对象实例的原型对象
运用原型对象的优点是可以让一切对象实例同享它所包含的属性和要领。换句话说,没必要在组织函数中定义对象实例的信息,而是可以将这些信息直接增加到原型对象中。
组织函、原型和实例的关联:每一个组织函数组织函数都有一个原型对象
(prototype)
,原型对象都包含一个指向组织函数的指针(constructor)
,而实例都包含一个指向原型对象的内部指针(__proto__)
明白原型对象!!!
只需建立了一个新函数,就会依据一组特定的规则为该函数建立一个
prototype
属性,这个属性指向函数的原型对象。
一切原型对象都邑自动取得一个constructor
(组织函数)属性,这个属性包含一个指向prototype
属性地点函数的指针。例:Person.prototype.constructor = Person
。
建立了自定义的组织函数以后,其原型对象默许只会取得constructor
属性;至于其他要领都是从Object继续而来的。
原型最初只包含
constructor
属性,而该属性也是同享的,因而可以经由历程对象实例接见
当挪用组织函数建立一个新实例后,该实例的内部将包含一个指针(内部属性)ECMA叫
[[Prototype]]
,指向组织函数的原型对象,高等浏览器在每一个对象上都支撑一个属性__proto__
。
注:这个衔接存在于实例与组织函数的原型对象之间,不存在于实例与组织函数之间。
可以经由历程
isPrototypeOf()
要领来肯定对象之间是不是存在这类关联Person.prototype.isPrototypeOf(person1); // true
;ES5新增了一个要领,叫
Object.getPrototypeOf()
,这个要领返回[[Prototype]]
的值。Object.getPrototypeOf(person1) == Person.prototype
。
运用这个要领可以轻易的取得一个对象的原型;
当为对象实例增加一个属性时,这个属性就会屏障原型对象中的同名属性;换句话说,增加这个属性只会阻挠我们接见原型中的谁人属性,但不会修正谁人属性。纵然将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的链接。不过,可以经由历程运用delete操作符则可以完整删除实例属性,从而让我们可以从新接见原型中的属性。
运用
hasOwnProperty()
要领可以检测一个属性是存在于实例中,还是存在于原型中。这个要领(从Object继续而来)只在给定属性存在于对象实例中,在返回true。
原型与in操作符
零丁运用in时,in操作符会在经由历程对象可以接见给定属性时返回true,不管该属性存在于实例中还是原型中。
同时运用
hasOwnProperty()
要领和in操作符,就可以肯定该属性究竟是存在于对象中,还是存在于原型中在运用for-in轮回时,返回的是一切可以经由历程对象接见的、可罗列的属性,个中即包含存在于实例中的属性,也包含存在于原型中的属性。屏障了原型中不可罗列属性的实例属性也会在for-in轮回中返回。
要取得对象上可罗列的实例属性,可以运用ES5的
Object.keys()
,这个要领吸收一个对象作为参数,返回一个包含一切可罗列属性的字符串数组。假如想得到一切实例属性,不管它是不是可罗列,都可以运用
Object.getOwnPropertyNames()
更简朴的原型语法
可以用一个包含一切属性和要领的对象字面量来重写全部原型对象
Person.prototype = {
name:'junyan',
job:'FE'
}
然则如许
constructor
属性就不再指向Person,这类语法现实上完整重写了默许的prototype
对象,因而constructor
属性就变成了新对象的constructor
属性,不再指向Person,而是指向Object。此时instanceof
还能返回准确的效果。假如
constructor
属性很主要,可以特地设定将它设置回恰当的值,但这类体式格局重设constructor
属性会致使它的[[Enumerable]]
特性被设置为true,由于默许情况下原生的constructor
属性是不可罗列的。可以运用ES5的Object.defineProperty()
原型的动态性
我们对原型对象所做的任何修正都可以马上从实例上反应出来,纵然是先建立了实例后修正原型也还是云云,由于实例与原型之间的衔接只不过是一个指针,而非一个副本;
然则假如重写了全部原型对象,就不一样。挪用组织函数时会为实例增加一个指向最初原型的
[[Prototye]]
指针(__proto__),而把原型修正为另一个对象就即是切断了组织函数与最初原型之间的联络重写原型对象切断了现有原型与任何之前已存在的对象实例之间的联络,它们援用的仍然是最初的原型
实例中的指针仅指向原型,而不指向组织函数
原生对象的原型
一切原生援用范例(Object、Array、String等)都在其组织函数的原型上定义了要领;
经由历程原生对象的原型,不仅可以取得一切默许要领的援用,而且也可以定义新要领。可以像修正自定义对象的原型一样修正原生对象的原型,因而可以随时增加要领。(不发起);
原型对象的题目
原型中一切属性被实例同享,关于基础范例值的属性,经由历程在实例上增加一个同名属性,可以隐蔽原型中的对应属性,但关于援用范例值的属性来讲,实例上修正其值,在一切其他实例中都邑反应出来,所以基础不零丁运用原型情势。
组合运用组织函数情势和原型情势
组织函数情势用于定义实例属性,而原型情势用于定义要领和同享属性。
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["xiaohua","hanghang"]
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name);
}
}
继续
ECMAScript只支撑完成继续,而且完成继续主如果依托原型链来完成。
原型链
原型链作为完成继续的主要要领。其基础思想是应用原型让一个援用范例继续另一个援用范例的属性和要领。
假如让原型对象即是另一个范例的实例,此时的原型对象将包含一个指向另一个原型的指针(__proto__)
响应地,另一个原型中也包含着一个指向另一个组织函数的指针。若另一个原型又是另一个范例的实例,上述关联依旧建立,云云层层递进,就构成了实例与原型的链条。
在找不到属性或要领的情况下,搜刮历程老是要一环一环地前行到原型链末尾才会停下来
默许的原型
一切援用范例默许都继续了Object,而这个继续也是经由历程原型链完成的。一切函数的默许原型都是Object实例,因而默许原型都邑包含一个内部指针(__proto__)
,指向Object.prototype。这也恰是一切自定义范例都邑继续toString()
、valueOf()
等默许要领的根本原因
肯定原型和实例的关联
第一种体式格局是运用
instanceof
操作符,只需用这个操作符来测试实例与原型链中涌现过的组织函数,效果就会返回true。第二种体式格局是运用
isPrototypeOf()
要领。一样,只需是原型链中涌现过的原型,都可以说是该原型链所派生的实例的原型,因而这个要领也会返回true.
郑重地定义要领
给原型增加要领一定要放在替代原型的语句以后,重写的要领会屏障本来的谁人要领
在经由历程原型链完成继续时,不能运用对象字面量建立原型要领。由于如许做就会重写原型链。
原型链的题目
实践中很少会零丁运用原型链
在经由历程原型来完成继续时,原型现实上会变成另一个范例的实例,本来的实例属性也就水到渠成的变成了如今的原型属性
在建立子范例的实例时,不能向超范例的组织函数中通报参数。应当说是没有办法在不影响一切对象实例的情况下,给超范例的组织函数通报参数。
借用组织函数
在子范例组织函数的内部挪用超范例组织函数,可以经由历程运用apply()
和call()
要领在新建立的对象上实行组织函数。
function SubType(){
SuperType.call(this);
}
这一年就会在新的Subtype对象上实行SuperType()
函数中定义的一切对象初始化代码。
可以在子范例组织函数中向超范例组织函数通报参数
function SubType(){
SuperType.call(this,'junyan');
}
假如仅仅是借用组织函数,那末也将没法防止组织函数情势存在的题目——要领都在组织函数中定义,因而函数复用就无从谈起,而且在超范例的原型中定义的要领,对子范例而言也是不可见的,效果一切范例都只能运用组织函数情势。
组合继续
偶然也叫作伪典范继续,运用原型链完成对原型属性和要领的继续,而经由历程借用组织函数来完成对实例属性的继续。如许既经由历程在原型上定义要领完成了函数复用,又可以保证每一个实例都有它自己的属性。
instanceof
和isPrototypeof()
也可以用于辨认基于组合继续建立的对象
组合继续最大的题目就是不管什么情况下,都邑挪用两次超范例组织函数:一次是在建立子类原型的时刻,另一次是在子范例组织函数内部。
原型式继续
这个要领没有运用严厉意义上的组织函数,是基于已有的对象建立新对象,同时还没必要因而建立自定义范例。
function object(o){
function F(){}
F.prototype = o;
return new F();
}
严厉上讲,
object()
对传入个中的对象实行了一次浅复制
ES5经由历程新增Object.create()
要领范例了原型式继续。这个要领吸收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义分外属性的对象。在传入一个参数的情况下,Object.create()
与object()
要领的行动雷同。
只想让一个对象与另一个对象坚持类似的情况下,原型式继续是完整可以胜任的,不要忘了,包含援用范例值的属性一直都邑同享响应的值,就像运用原型情势一样。
寄生式继续
建立一个仅用于封装继续历程的函数,该函数在内部以某种体式格局来加强对象,末了再像真地是它做了一切事情一样返回对象。
function createAnother(original){
var clone = object(original);
clone.sayHi = function(){
console.log('Hi');
};
return clone;
}
在主要斟酌对象而不是自定义范例和组织函数的情况下,寄生式继续也是一种有效的情势。任何可以返回新对象的函数都适用于这个情势
寄生式组合继续
所谓寄生组合式继续,即经由历程借用组织函数来继续属性,经由历程原型链的混成情势来继续要领。其背地的基础思路是:没必要为了指定子范例的原型而挪用超范例的组织函数,我们所须要的不过就是超范例原型的一个副本罢了。本质上,就是运用寄生式继续来继续超范例的原型,然后再将效果指定给子范例的原型
function inheritPrototype(subType,superType){
var prototype = Object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
第一步是建立超范例原型的一个副本
第二部是为建立的副本增加constructor属性,从而填补因重写原型而落空的默许的constructor属性。
将新建立的对象(即副本)赋值给子范例的原型
这类继续的高效率体如今它只挪用了一次超范例组织函数,而且因而防止了在子范例.prototype上面建立没必要要的、过剩的属性。于此同事,原型链还能坚持稳定;因而还能一般运用
instanceof()
和isPrototypeOf()