JavaScript高程第六章:继续-明白与实践

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运用.

建立对象

  1. 工场情势

  2. 组织函数情势

  3. 原型情势 – 引伸出原型对象的邃晓

  4. 组合情势 处理原型情势题目

  5. 动态原型情势

  6. 寄生组织函数情势

  7. 稳妥组织函数情势

工场情势

瑕玷:未处理辨认题目(怎样晓得一个对象的范例)

示例:

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示例我们会有以下步骤.

  1. 建立一个新对象

  2. 将组织函数作用域赋给新对象(this指向)

  3. 实行组织函数中的代码(为新对象增加属性)

  4. 返回新对象
    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的对象
和寄生组织函数情势的相似点:

  1. 建立对象实例不援用this

  2. 不运用new操作符挪用组织函数

  3. 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中支撑的是完成继续,而且其完成继续重要依托原型链完成,所以邃晓原型链就很重要了.

  1. 原型链

  2. 借用组织函数

  3. 组合继续

  4. 原型式继续

  5. 寄生式继续

  6. 寄生组合式继续

原型链

基本思想:应用原型链让一个援用范例继续另一个援用范例的属性和要领.
注!和我们之前提到的一样,一切函数的默许原型都是Object的实例.内部指针->Object.prototype

原型与实例的关联

instanceof操作符,能够测试实例与原型链中的组织函数.
isPrototypeOf()要领 ,与instanceof操作符返回结果雷同.

郑重定义要领

子类重写超类/父类中某个要领,或许增加父类/超类不存在的某个要领时,要放在替代原型语句后.
注!不要运用对象字面量建立原型要领,这会重写原型链

原型链题目

  1. 援用范例题目

  2. 建立子范例实例时不能(或许说没办法在不影响一切对象实例的情况下)向超范例的组织函数通报参数.

依据上述题目,实践中很少零丁运用原型链.

示例:

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中最经常使用的继续情势,而且instanceofisPrototypeOf()都能够辨认

示例:

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的原型)初始化属性.第二次时:新对象上又新建立了雷同的属性,因而这两个属性就屏障了原型中两个同名属性.

处理要领就是寄生组合式继续.经由过程借用组织函数来继续属性,经由过程原型链的混成情势来继续体式格局.
基本思绪:没必要为了指定子范例的原型而挪用超类/父类的组织函数,我们须要的学问超类/父类原型的一个副本.在这点上运用寄生式继续来继续超类/父类的原型,再将结果指定给子类的原型.

高效力体如今防止了建立过剩没必要要的属性,原型链还能坚持稳定.instanceofisPrototypeOf()都能一般运用.

能够说寄生组合式继续是援用范例最理想的继续范式,这也被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
    原文作者:thewindsword
    原文地址: https://segmentfault.com/a/1190000006982733
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞