JavaScript高程第六章:继续-邃晓与实践
昨日细细的读了一遍JavaScript高程,如今写篇文章来稳固下认知吧.
读
首先是从中读到了什么,我本身也在念书的时刻用笔记下了各个部份的点,如今即是浏览笔记回想下书籍.
邃晓基本
ECMA-262(第五版)
ECMA中划定了两种属性:数据属性 and 接见器属性
数据属性
包含一个数据值的位置(读取和写入)
4个形貌行动的特征
[[Configurable]] 默许值为true,形貌了可否delete,可否修正其特征(变更为接见器属性)
[[Enumerable]] 默许值为true,形貌了可否经由过程for-in轮回返回属性.
[[Writable]] 默以为true,可否修正属性的值
[[Value]] 默以为undefined,就是属性的值
相干函数 Object.defineProperty(属性地点对象,属性名,形貌符对象(可多个,{}))
注!修正configurable为false,则对后续挪用该要领有限定,变得只能修正Writable和Value特征.
接见器属性
不包含属性值,包含一对getter和setter函数(非必需),一样有4个特征,雷同功用不多加诠释.
[[Configurable]] 默许值为true
[[Enumerable]] 默许值为true
[[Get]] default:undefined getter函数
[[Set]] default:undefined setter函数
注!接见器属性不能直接定义,必需运用Object.defineProperty()
定义,在严厉情势中,尝试写入只指定了getter
函数的属性会抛出毛病,尝试读取只指定了setter
函数的属性同理.
非严厉情势中,则会疏忽/返回undefined
相干函数和兼容
Object.defineProperty(属性地点对象,属性名,形貌符对象(可多个,{}))
支撑:IE9+(IE8部份完成),Firefox4+,Safari5+,Opera 12+和Chrome
不兼容处理方案:__defineGetter__(属性名,函数)
,__defineSetter__(属性名,函数)
然则没法处理对[[Configurable]]和[[Enumerable]]的修正
Object.defineProperties(对象,{属性1:{形貌符},属性2:{}...})
支撑:IE9+(IE8部份完成),Firefox4+,Safari5+,Opera 12+和Chrome
Object.getOwnPropertyDescriptor(对象,属性名)
返回:对象(接见器/花样)
能够对JS中任何对象,包含BOM,DOM运用.
建立对象
工场情势
组织函数情势
原型情势 – 引伸出原型对象的邃晓
组合情势 处理原型情势题目
动态原型情势
寄生组织函数情势
稳妥组织函数情势
工场情势
瑕玷:未处理辨认题目(怎样晓得一个对象的范例)
示例:
function makePerson(name,age,job){
var o =new Object();
o.name = name;
o.age = age;
o.job = job;
o.arr = ["a","b"];
o.sayName = function(){
alert(this.name);
}
return o;
}
var a = makePerson("jack",18,"programmer");
var b = makePerson("james",20,"designer");
a.arr.push("c");
console.log("a:"+a.arr); //a:a,b,c
console.log("b:"+b.arr); //b:a,b
console.log(a instanceof makePerson);//false
console.log(b instanceof makePerson);//false
console.log(a.prototype); //undefined
console.log(b.prototype); //undefined
console.log(a.prototype); //undefined
console.log(b.prototype); //undefined
组织函数情势
应当值得注重的是组织函数我们是大写字母开首,这是约定俗成的.建立一个Person示例我们会有以下步骤.
建立一个新对象
将组织函数作用域赋给新对象(this指向)
实行组织函数中的代码(为新对象增加属性)
返回新对象
而instanceof
操作符和constructor
属性都能让我们分辨出这是一种特定的范例,这也是组织函数情势赛过工场情势的处所.
假如直接作为一般函数挪用,则会将属性赋值给window
对象(Global
)
题目:函数不复用题目,实例中的要领不是同一个Function的实例,审定要领.console.log(a.sayName == b.sayName)
处理:放到全局定义,组织函数中设置即可
致使新题目:毫无封装性,而为了处理这些题目,我们能够运用后续的原型情势来处理.
注!一切对象都继续自Object
,所以a,b运用instanceof
操作符推断是不是为Object
的实例是true
.
示例:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.arr = ["a","b"];
this.sayName = function(){
alert(this.name);
};
}
var a = new Person("jack",18,"programmer");
var b = new Person("james",20,"designer");
a.arr.push('c');
console.log("a:"+a.arr); //a:a,b,c
console.log("b:"+b.arr); //b:a,b
console.log(a instanceof Person);//true
console.log(b instanceof Person);//true
console.log(a.prototype); //undefined
console.log(b.prototype); //undefined
console.log(a.constructor); //[Function: Person]
console.log(b.constructor); //[Function: Person]
原型情势
每个function
都有一个prototype
(原型)属性,为一个指针,指向一个对象(用处:包含能够由特定范例的一切实例同享的属性和要领).
经由过程prototype
设置的属性和要领都是同享的,接下来让我们邃晓一下原型对象.
邃晓原型对象
在任何时刻,我们建立一个新函数都意味着我们会依据一个特定划定规矩建立prototype
属性,该属性指向函数的原型对象.
在默许情况下,一切原型对象都邑自动取得一个constructor
(组织函数)属性,这个属性包含一个指向prototype
属性地点函数的指针.
挪用组织函数建立一个实例后,实例内部包含一个指针[[Prototype]] (Firefox,Safari,Chrome接见运用__proto__
),
关于推断能够运用Person.prototype.isPrototypeOf(a)
函数.Person的prototype
为a的prototype
Object.getPrototypeOf
能够接见[[Prototype]]的值.
值得注重的是,我们能够经由过程对象实例来接见保留在原型的值,然则我们不能经由过程对象实例重写原型的值(对象.属性 = 值
,如许是增加属性到实例,掩盖屏障了原型的值罢了,并没有重写,然则关于援用范例差别,纵然设置对象.属性=null
也是不会恢复其指向,只是在实例中写入属性.对象为null
罢了,要想恢复,能够运用delete
操作符)
原型与in操作符
体式格局一:for-in
轮回中运用
体式格局二:零丁运用,会在能接见(不论经由过程对象照样原型)给定属性时返回true
(一切能经由过程对象接见,可罗列的属性)
一切开发人员定义的属性都是可罗列的(IE8以及更早破例,个中屏障的不可罗列属性的实例属性不会出如今for-in
轮回中)
相干函数:a.hasOwnProperty(属性名)
,能够肯定属性是不是存在于实例中,是则返回true
var keys = Object.keys(Person.prototype)
变量中保留一个数组,Object.keys
返回的是一个包含一切可罗列属性的字符串数组.Object.getWenPropertyNames()
能够猎取一切实例属性(不管是不是可罗列)
更简朴的原型语法
Person.prototype = {
name : "Nicholas",
age: 29,
job: "software engineer",
sayName:fuinction(){
alert("this.name");
}
}
在上面代码中,我们相当于完整重写了prototype
对象,同时其constructor
不再指向Person
(指向Object组织函数),只管instanceof
操作符能返回准确结果,然则constructor
已没法肯定对象范例了.固然我们能够本身在新建对象时刻设置constructor: Person
,然则如许做会致使它变成可罗列属性(原生不可罗列,处理要领:Object.defineProperty()
).
原型的动态性
运用上述原型语法,会割断组织函数与最初原型的联络.
如var friend = new Person()
出如今完整重写之前,则我们没法经由过程friend
接见重写的原型.
function Person(){
}
var friend = new Person();
Person.prototype = {
constructor: Person,
name: "Jack",
age: 29,
job: "programmer",
sayName:function(){
console.log(this.name);
}
}
console.log(friend.age); // undefined
friend.sayName(); //报错
friend中的[[Prototype]]指向的仍然是本来的空无一物的Prototype,而不是我们厥后重写的原型对象.
原生对象的原型
原生援用范例(Object
,Array
,String等
)都采纳原型情势建立
注!不引荐修正原生对象的原型,能够致使定名争执/重写原生要领.
原型对象的题目
同享援用范例值的属性,如Array
,修正则会同享
示例:
function Person(){
}
Person.prototype.name = "Jack";
Person.prototype.age = 18;
Person.prototype.job = "Software Engineer";
Person.prototype.arr = ["a","b"];//援用范例
Person.prototype.sayName = function(){
console.log(this.name);
}
var a = new Person();
a.sayName(); //Jack
var b = new Person();
b.name = "James";//建立值,屏障了原型的值
console.log(b.age);//18
console.log(b);//Person { name: 'James' }
b.sayName();//James
console.log(a.sayName == b.sayName);//true
a.arr.push("c");//修正援用范例
console.log("a:"+a.arr); //a:a,b,c
console.log("b:"+b.arr); //b:a,b,c
console.log(a instanceof Person);//true
console.log(b instanceof Person);//true
console.log(a.prototype); //undefined
console.log(b.prototype); //undefined
console.log(a.constructor); //[Function: Person]
console.log(b.constructor); //[Function: Person]
组合运用组织函数情势和原型情势
处理原型情势的题目-同享援用范例值的属性
个中特性在于,实例属性在组织函数中定义,同享的constructor
与要领在原型中定义,以下.
现在来讲最普遍,认同度最高的一种体式格局来建立自定义范例.
示例:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["a", "b"];
}
Person.prototype = {
constructor:Person,
sayName:function(){
console.log(this.name);
}
}
var a = new Person("jack",18,"programmer");
var b = new Person("james",20,"designer");
a.friends.push('c');
console.log(a.friends);//a,b,c
console.log(b.friends);//a,b
console.log(a.friends === b.friends); //false
console.log(a.sayName === b.sayName); //true
动态原型情势
在组织函数中,if搜检初始化后应存在的任何属性或要领.从而对组织函数和原型要领举行封装.
示例:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["a","b"];
//注重不要运用对象字面量重写原型
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var a = new Person("jack",18,"programmer");
var b = new Person("james",20,"designer");
a.friends.push("c");
console.log(a.friends);//a,b,c
console.log(b.friends);//a,b
console.log(a.friends === b.friends);//false
console.log(a.sayName === b.sayName);//true
console.log(a instanceof Person);//true
console.log(b instanceof Person);//true
寄生组织函数情势(不引荐
相当于工场情势,一般用于在特别情况下为对象建立组织函数,如我们要建立一个具有分外要领的特别数组,又不能直接修正Array
组织函数,就能够运用该情势.
注!返回对象和组织函数外部建立对象没有差别,所以没法肯定对象范例.不引荐运用
示例:
function SpecialArray(){
var values = new Array();
//增加值
values.push.apply(values,arguments);
//增加要领
values.toPipedString = function(){
return this.join("|");
};
return values;
}
稳妥组织函数情势(不引荐
稳妥对象:没有大众属性,要领都不援用this的对象
和寄生组织函数情势的相似点:
建立对象实例不援用this
不运用new操作符挪用组织函数
instanceof无效
注重,稳妥对象中,除了定义的要领以外没有其他要领接见某值.
注!和寄生组织函数情势一样,不引荐运用
示例:
function Person(name,age,job){
var o = new Object();
o.sayName = function(){
alert(name);
};
return 0;
}
var friend =Person("Jack",18,"Software Enginner");
friend.sayName();
继续
在ECMAScript中支撑的是完成继续,而且其完成继续重要依托原型链完成,所以邃晓原型链就很重要了.
原型链
借用组织函数
组合继续
原型式继续
寄生式继续
寄生组合式继续
原型链
基本思想:应用原型链让一个援用范例继续另一个援用范例的属性和要领.
注!和我们之前提到的一样,一切函数的默许原型都是Object
的实例.内部指针->Object.prototype
原型与实例的关联
instanceof
操作符,能够测试实例与原型链中的组织函数.isPrototypeOf()
要领 ,与instanceof
操作符返回结果雷同.
郑重定义要领
子类重写超类/父类中某个要领,或许增加父类/超类不存在的某个要领时,要放在替代原型语句后.
注!不要运用对象字面量建立原型要领,这会重写原型链
原型链题目
援用范例题目
建立子范例实例时不能(或许说没办法在不影响一切对象实例的情况下)向超范例的组织函数通报参数.
依据上述题目,实践中很少零丁运用原型链.
示例:
function SuperType(){//父类/超类
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){//子类
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
return this.subproperty;
}
// 郑重定义要领
//SubType.prototype.getSuperValue=function(){
// return false;
//}该要领会屏障本来的要领,即经由过程SuperType的实例挪用getSuperValue时依旧挪用本来的要领,而经由过程SubType的实例挪用时,会实行这个从新定义的要领.必需在SubType.prototype = new SuperType();以后,再定义getSubValue和该要领.
var a = new SubType();
console.log(a.getSubValue());//false
console.log(a.getSuperValue());//true
//原型与实例的关联
console.log(a instanceof Object);//true
console.log(a instanceof SuperType);//true
console.log(a instanceof SubType);//true
借用组织函数
捏造对象/典范继续.
目标:处理援用范例题目->借用组织函数(constructor stealing)
基本思想:子范例组织函数内部挪用超类/父类组织函数
瑕玷:没法防止组织函数情势存在的题目(函数没法复用)
所以该体式格局很少零丁运用.
示例:
function SuperType(name){
this.name = name;
this.arr = ["a","b","c"];
}
function SubType(){
SuperType.call(this,"jack");//通报参数
this.age = 18;//实例属性
}
var a = new SubType();
a.arr.push("d");
var b = new SubType();
console.log(a.arr);//a,b,c,d
console.log(b.arr);//a,b,c
组合继续
combination inheritance
也称伪典范继续,将原型链和借用组织函数手艺连系一同的继续情势.
基本思想:运用原型链完成对原型属性和要领的继续,借用组织函数完成对实例属性的继续.constructor
重指向
相当于:属性继续(借用组织函数),函数外定义要领,constructor
从新指向
组合继续防止了原型链和借用组织函数的缺点,融会了长处,成为了JS中最经常使用的继续情势,而且instanceof
和isPrototypeOf()
都能够辨认
示例:
function SuperType(name){
this.name = name;
this.arr = ["a","b"];
}
SuperType.prototype.sayName =function(){
console.log(this.name);
};
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
//inherit
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
console.log(this.age);
};
var a = new SubType("Jack",18);
a.arr.push("c");
console.log(a.arr);//a,b,c
a.sayName();//Jack
a.sayAge();//18
var b = new SubType("James",20);
console.log(b.arr);//a,b
b.sayName();//James
b.sayAge();//20
原型式继续
Prototypal inheritance
将传入的对象作为函数内定义的组织函数的原型(请求必需有一个对象能够作为另一个对象的基本),在ECMAScript5中新增Object.create()
要领范例了原型式继续,它吸收两个参数,一个用作新对象原型的对象和(可选)一个为新对象定义分外属性的对象.
单个参数情况下Object.create()
和Object()
行动雷同
兼容性:IE9+,Firefox4+,Safari5+,Opera12+,Chrome
瑕玷:和原型情势一样,援用范例同享.
示例:
function object(o){
function F(){};
F.prototype = o;
return new F();
}
var person = {
name: "Jack",
arr: ["a","b"]
};
var a = object(person);
var b = object(person);
a.name = "James";
a.arr.push("c");
b.name = "Ansem";
b.arr.push("d");
console.log(person.arr);//a,b,c,d
console.log(a.arr);//a,b,c,d
console.log(b.arr);//a,b,c,d
//Object.create
var person2 = {
name: "Jack",
arr: ["a","b"]
};
var c = Object.create(person2,{
name:{
value: "James"
}
});
var d = Object.create(person2,{
name:{
value: "Ansem"
}
});
c.arr.push("c");
d.arr.push("d");
console.log(c.name);//James
console.log(d.name);//Ansem
console.log(person.arr);//a,b,c,d
console.log(c.arr);//a,b,c,d
console.log(d.arr);//a,b,c,d
寄生式继续
parasitic inherit
思绪与寄生组织函数和工场情势相似,建立新对象,加强对象,返回对象.
瑕玷:函数复用不了,关于援用范例为同享.
示例:
function createAnother(original){
var clone = object(original);
clone.sayHi = function(){
console.log("HI");
};
return clone;
}
寄生组合式继续(重点)
组合继续的题目:不管什么情况都邑两次挪用超范例组织函数
第一次:SubType.prototype = new SuperType()
时
第二次:new SuperType()
内->SuperType.call(this,name);
这形成的结果是,第一次时:SuperType的实例(SubType的原型)初始化属性.第二次时:新对象上又新建立了雷同的属性,因而这两个属性就屏障了原型中两个同名属性.
处理要领就是寄生组合式继续.经由过程借用组织函数来继续属性,经由过程原型链的混成情势来继续体式格局.
基本思绪:没必要为了指定子范例的原型而挪用超类/父类的组织函数,我们须要的学问超类/父类原型的一个副本.在这点上运用寄生式继续来继续超类/父类的原型,再将结果指定给子类的原型.
高效力体如今防止了建立过剩没必要要的属性,原型链还能坚持稳定.instanceof
和isPrototypeOf()
都能一般运用.
能够说寄生组合式继续是援用范例最理想的继续范式,这也被YUI库所采纳.
示例:
//基本情势
function inheritPrototype(subType,superType){
var prototype = Object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name){
this.name = name;
this.arr = ["a","b"];
}
SuperType.prototype.sayName =function(){
console.log(this.name);
};
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
};
inheritPrototype(SubType,SuperType);//防止了屡次实行,提高了效力
SubType.prototype.sayAge = function(){
console.log(this.age);
};
var c = new SubType("Jack",18);
var d = new SubType("Ansem",25);
c.arr.push("c");
d.arr.push("d");
console.log(c.name);//Jack
console.log(d.name);//Ansem
console.log(c.arr);//a,b,c
console.log(d.arr);//a,b,d