《JavaScript高等程序设计》——对象进修笔记

建立对象

运用对象字面量的情势一个接口会建立许多对象, 会发作大批的反复代码。

工场形式:用函数来封装以特定接口建立对象的细节

function createPerson (name,age,job)
{
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName =function()
    {
        alert(this.name);
    }
    return o;
}

var person1 = createPerson("Simon", 29, "software Engineer");
var person2 = createPerson("Zaynex",22, "Doctor");
  • 这类形式处理了建立多个相似对象的题目,但却没有处理对象辨认的题目(即如何晓得一个对象的范例)

组织函数形式

- 可用于建立特定形式的对象,像Object、Array等原生组织函数,在运行时会自动涌现在执行环境中。

我们运用组织函数重写下适才的函数。

function createPerson(name, age, job)
    {
        this.name = name;
        this.age = age;
        this.job = job;
        this.sayName = function()
        {
            alert(this.name);
        };
    }

    var person1 = new Person("Simon",29, "software Engineer");
    var person2 = new Person("Simon",29, "software Engineer");

组织函数与工场形式的差别

  1. 没有显现地建立对象;

  2. 直接将属性和要领赋给this对象;

  3. 没有 return 语句;

我们注重到Person开首是大写,依据通例来讲,组织函数开首字母是大写,非组织函数以小写字母开首。

挪用组织函数四步骤

  1. 建立一个新对象;

  2. 将组织函数的作用域赋给新对象(因而this就指向了这个新对象);

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

  4. 返回新对象

person1和person2都保留着Person的一个差别的实例。这两个对象都有一个constructor(组织函数)属性,该属性指向Person。

alert(person1.constructor == Person) //true;
alert(person2.constructor == Person) //true;

对象的constructor属性最初是用来标识对象范例的。然则提到检测对象范例,照样instanceof操作符更牢靠一些。

alert(person1 instanceof Object);
alert(person1 instanceof Person);
alert(person2 instanceof Object);
alert(person2 instanceof Person);
//都为true.

我们所建立的一切对象都是Object的实例,同时也是Person的实例。

  • 建立自定义的组织函数意味着未来可以作为实例标识为一种特定的范例;组织函数形式赛过工场形式的处所

把组织函数当函数

- 任何函数,只需经由历程  new 操作符来挪用,那它就可以作为组织函数;

//当作组织函数运用

var person = new Person("Simon", 29, "software Engineer");
person.sayName(); //Simon

//平常函数挪用
Person("Genf", 23, "DOCTOR");  //增加到window
window.sayName();  // Genf
以适才的那种体式格局定义的组织函数定义在Global对象中(在浏览器中是window对象),在全局作用域中挪用函数时,this指向的是window对象

// 在别的一个对象的作用域中挪用
var o = new Object();
Person.call(o, "Kristen", 25, 'nusd');
o.sayName(); // Kristen    

组织函数的缺点

每一个要领都要在每一个实例上从新建立一遍。

person1 和 person2 都有一个名为 sayName() 的要领;但那两个要领都不是同一个 Function 的实例,因而会有差别的作用域链和标识符剖析;差别实例上的同名函数是差别的。

不要忘了,每一个函数都是一个对象!所以sayName要领也可以如许写,因而每一个Person实例都包括着差别的Function实例。以这类体式格局建立函数,会致使差别饿作用域和标识符剖析。

this.sayName = new Function("alert(this.name)");  //与声明函数在逻辑上是等价的

我们可以磨练下

alert(person1.sayName() == person2.sayName) //false;

建立两个完成雷同使命的Function 实例没有必要,何况有this对象在,基础不用在执行代码前就把函数绑定到特定对象上面。

  • 我们可以经由历程函数定义转移组织函数外部来处理这个题目。

    function Person(name, age ,job)

    {
        this.name = name;
        this.age = age;
        this.sayName = sayName;
    }
    
    function sayName()
    {
        alert(this.name);
    }
    
    var person1 = new Person("Simon", 29, "software Engineer");
    var person2 = new Person("Zaynex", 29, "DOCTOR");
    

把sayName()函数的定义转移到了组织函数外部。

在组织函数内部,将sayName属性设置成即是全局的 sayName 函数。如许sayName 包括的是一个指向函数的指针。 person1和person2同享同一个sayName()函数。

但题目是:
在全局作用域中定义的函数实际上只能被某个对象挪用,假如对象须要定义许多要领,那末就要定义多个全局函数。

因而我们须要用原型形式来处理这个题目。

原型形式

我们建立的每一个函数都有一个 prototype(原型) 属性,这个属性属于指针,指向一个对象,而这个对象的用处是包括可以由特定范例的一切实例的同享的属性和要领。即经由历程组织函数而建立的谁人对象实例的原型对象。

我们不势必组织函数定义对象实例的信息中,而是可以将这些信息直接增加到对象原型中。

function Person(){
    }

    Person.prototype.name ="Simon";
    Person.prototype.age = 29;
    Person.prototype.job = "software Engineer";
    Person.prototype.sayName = function(){
        alert(this.name);
    };


    var person1 = new Person();
    person1.sayName();

    var person2 = new Person();
    person2.sayName();

    alert(person1.sayName == person2.sayName);
    

实际上,person1和person2都不包括属性和要领, 但可以挪用person1.sayName().这是经由历程查找对象属性的历程来完成的。

明白原型对象

不管什么时候,只需建立了新函数,就会依据一组特定的规则为该函数建立一个 prototype 属性,这个属性指向函数的原型对象。

在默许状况下,一切原型都邑自动获得一个constructor(组织函数)属性,这个属性包括在一个指向 prototype属性地点的函数的指针。举例说明: Person.prototype.constructor 指向Person.

建立了自定义组织函数今后,其原型对象默许只会获得 constructor 属性。其他要领都是从Object继承来的。

挪用组织函数的一个实例后,该实例内部将包括一个指针(ES5中称为[[Prototype]],指向组织函数的原型对象。在剧本中没有规范情势接见[[Prototype]],但在FF,SF,Chrome中的每一个对象都支撑属性_proto_;在其他完成中,该属性对剧本不可见。

要明白的是, 这个链接存在于实例与组织函数的原型对象之间,而非实例与组织函数之间。

虽然在实际中无法接见到[[Prototype]],但可以经由历程 isPrototypeOf()来肯定是不是存在这类关联。
在ES5中新增一个要领,运用 Object.getPrototypeOf()可以轻易的猎取一个对象的原型


每当代码读取某个对象的某个属性时,都邑执行一次搜刮,
1.先从实例自身最先搜刮属性,存在,搜刮完毕。若不存在,执行2
2.从实例的原型最先搜刮属性。

继承适才的代码。假如我们继承给实例增加雷同的属性,会如何?

function Person(){
    }

    Person.prototype.name ="Simon";
    Person.prototype.age = 29;
    Person.prototype.job = "software Engineer";
    Person.prototype.sayName = function(){
        alert(this.name);
    };


    var person1 = new Person();
    var person2 = new Person();
    person1.name = "xiwenzheng";

    alert(person1.name) //xiwenzheng  ——来自实例
    alert(person2.name) // Simon  ——来自原型
    

在person1这个实例中重写属性,那末诠释器搜刮到了实例自身的属性直接返回,关于person2而言,实例中没有属性,那末再往实例的原型最先搜素属性。

  • 给对象增加一个属性时,这个属性就会屏障原型对象中保留的同名属性,就是阻挠我们接见原型对象,但并不会修正原型对象中的同名属性。纵然将person1.name 设置为 null 也不会影响原型对象中的同步属性。

  • 不过delete 实例属性,就可以接见原型对象中的属性了。

    function Person(){

        }
    
        Person.prototype.name ="Simon";
        Person.prototype.age = 29;
        Person.prototype.job = "software Engineer";
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    
        var person1 = new Person();
        var person2 = new Person();
        person1.name = "xiwenzheng";
        alert(person1.name); //xiwenzheng  ——来自实例
        alert(person2.name); // Simon  ——来自原型
        delete person1.name;
        alert(person1.name); // Simon 来自原型
        

    运用hasOwnProperty()可以检测一个属性是不是存在实例中照样存在原型中,这个要领只在给定属性存在于对象实例中才会返回 true;

我们继承采纳适才删除部份的整段代码。

alert(person1.hasOwnProperty("name")); // 返回false

本来person1.name是存在对象实例中的(被我们设为了”Zaynex”),然则被我们delete了。
假如我们不delete的话,那就是true了。要想获得原型属性的描述符,必需要在原型对象上挪用 Object.hasOwnPropertydDsecriptor();

原型与 in 操作符

in 操作符会在经由历程对象可以接见给定属性时返回 true ,不管该执行存在于实例中照样原型中。

  • 运用in:推断是不是有该属性

  • 运用hasOwnProperty()推断是不是存在对象实例中;

  • 连系今后就可以推断该属性是在原型中照样在实例中。

    function hasPrototypeProperty(object, name ){

    return !object.hasOwnProperty(name) && (name in object);

    }
    person1.name = “Zaynex”;
    alert(hasPrototypeProperty(person1, “name”)); //false;存在实例中

    for-in 轮回时,返回的都是经由历程对象接见的、可罗列的属性(行将[[Enumberable]]标记为true的属性),在ES5中constructor 和 prototype属性的 [[Enumberable]]

设为false,但并非一切浏览器都照此完成。
想获得对象上一切可罗列的实例属性,可以运用Object.Keys()要领。

function Person(){
    }

    Person.prototype.name ="Simon";
    Person.prototype.age = 29;
    Person.prototype.job = "software Engineer";
    Person.prototype.sayName = function(){
        alert(this.name);
    };

    var keys = Object.keys(Person.prototype);
    alert(keys);//  name ,age, job, sayName

    var p1 = new Person();
    p1.name = "Rob";
    p1.age = 29;

    var p1keys = Object.keys(p1);
    alert(p1keys);  // name ,age 
    

假如想得到一切实例属性,不管是不是可罗列,都可以运用 Object.getOwnPropertyNames()

var keys = Object.keys(Person.prototype);
alert(keys);//  constructor, name ,age, job, sayName

更简朴的原型语法

之前的例子中每增加一个属性和要领都要 Person.prototype,我们举行恰当的封装。

function Person(){

}

Person.prototype = {
    name : "Simon",
    age : 29;
    job : "software Engineer",
    sayName : function  () {
        alert(this.name);
    }
};

我们将 Person.prototype 设置为即是一个以对象字面量情势建立的新对象。
之前引见到,每建立一个函数,同时会建立它的prototype对象,这个对象会指定获得constructor 属性。而我们在这里的语法本质上是重写了默许的 prototype 对象。

所以 constructor属性也编程了新对象的属性。(指向Object组织函数),不再指向Person了。

instanceof 测试 Object 和 Person 都返回 true,但constructor 属性则即是Object而不即是 Person ;

假如 constructor 的值很主要,则可以特地设置回恰当的值

function Person(){
}

Person.prototype = {
    constructor:Person,
    name : "Simon",
    job : "software Engineer",
    sayName : function () {
        alert(this.name);
    }
}
  • 注重,以这类体式格局重设constructor属性会致使 [[Enumberable]]特征设置为true,但默许我们是不可罗列constructor属性的。

为了兼容ES5的JS引擎,可以用 Object.defineProperty();

function Person(){
    }

    Person.prototype = {
        name : "Simon",
        job : "software Engineer",
        sayName : function () {
            alert(this.name);
        }
    }
Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
});

原型的动态性

在原型中找值的历程是一次搜刮,因而我们对原型对象所做的任何修正都能马上从实例中回响反应出来——纵然是先建立实例后修正原型。
不信你看:

var friend = new Person();
Person.prototype.sayHi = function(){
    alert("hi");
};
friend.sayHi();  // "hi" 
  • 这个可以归结于实例与原型之间的松懈链接关联。我们起首会在实例中搜刮sayHi的属性,在没找到的状况下会继承搜刮原型,因为实例与原型之间的链接只不过是一个指针。

然则假如重写悉数原型对象,状况就不一样了。挪用组织函数时会为实例增加一个指向最初原型的[[Prototype]]指针,而把原型修正为别的一个对象就即是切断了组织函数与最初原型之间的联络。

请记着,实例中的指针仅指向原型,而不指向组织函数

function Person(){

}

var  friend = new Person();

Person.prototype = {
    constructor:Person,
    name : "Simon",
    job : "software Engineer",
    sayName : function () {
        alert(this.name);
    }
};

friend.sayName();  //error

在这个例子中,我们建立了Person的实例,然后又重写了其原型对象,然后在挪用sayName()时发作毛病,因而friend指向的原型不包括以该名字定名的属性。
《《JavaScript高等程序设计》——对象进修笔记》

原型对象的题目

  1. 省略了为组织函数通报初始化参数这一环节,效果一切实例在默许状况下都获得雷同的属性值。

  2. 同享致使的题目,许多属性可以同享,关于包括援用范例值的属性来讲,题目比较突出。

    function Person(){

    }
    
    Person.prototype = {
        constructor:Person,
        name : "Simon",
        job : "software Engineer",
        friends : ["Shelby", "Court"],
        sayName : function () {
            alert(this.name);
        }
    };
    
    var  person1 = new Person();
    var person2 = new Person();
    
    person1.friends.push("Van");
    
    alert(person1.friends === person2.friends) // true;
    

修正person1.friends 援用的数组,增加字符串,因为 friends数组存在 Person.prototype 而非 person1中,所以修正也会形成person2.friends反应出来,假如我们的初志就是要同享同一个数组,那无话可说。

但是平常都是要有属于本身的悉数属性的。而这个题目恰是我们很少看到有人零丁运用原型形式的缘由。

组合运用组织函数形式和原型形式

建立自定义范例的最常见体式格局就是这个。组织函数用于定义实例属性,原型形式用于定义要领和同享的属性。如许,每一个实例都邑有本身的一份实例属性的副本,但又同事同享着对要领的援用,最大限制节省了内存。另外,这类混成形式还支撑向组织函数通报参数。

function Person (name, age, job){
       this.name = name;
       this.age = age;
       this.job = job;
       this.friends = ["Shelby", "Court"];
   }

   Person.prototype = {
       constructor : Person,
       sayName : function (){
           alert(this.name);
       }
   }


   var person1 = new Person("Nicholas", 29, "software Engineer");
   var person2 = new Person("Greg", 27, "DOCTOR");

   person1.friends.push("Van");
   alert(person1.friends);  // Shelby,Court,Van
   alert(person2.friends); // shelby,Court
   alert(person1.friends === person2.friends); // false
   alert(person1.sayName === person2.sayName); // true
   

实例属性都是在组织函数中定义的,一切实例同享的属性是在 constructor 和要领sayName()是在原型中定义的。

动态原型形式

当其他OO言语履历开发人员看到自力的组织函数和原型时,会觉得疑心。因而涌现了 动态原型形式———即把一切信息都封装在了组织函数中,而经由历程在组织函数中初始化原型(仅在必要的状况下),又坚持了同事运用组织函数和原型的长处。

换句话说,可以经由历程搜检某个应当存在的要领是不是有用,来决议是不是须要初始化原型。

 function Person (name, age, job){
    //属性
       this.name = name;
       this.age = age;
       this.job = job;
       this.friends = ["Shelby", "Court"];

       if( typeof this.sayName != "function"){
           Person.prototype.sayName = function() {
               alert(this.name);
           };
       }
 }
    // 只要在sayName不存在的状况下, 才将其增加到原型中,这段代码只会在首次挪用函数时执行。今后原型已完成初始化,不须要再做修正。
 var  friends1 = new Person("Nicholas", 29, "software Engineer");
 var friends2 = new Person("Zaynex",19,"Engineer");
 friends1.sayName();
 friends2.sayName();

因为第一次当friends1初始化今后,friends2就不须要再举行初始化原型。
概况参考点击此处

寄生组织函数形式

在上述几种形式都不实用的状况下,我们可以运用寄生组织函数形式
基本思想:建立一个函数,该函数的作用仅仅是封装对象的代码,然后再返回新建立的对象。

 function Person (name, age, job){
       var o = new Object();
       o.name = name;
       o.age = age;
       o.job = job;
       o.sayName = function(){
           alert(this.name);
       };
       return o;
   }

   var friend = new Person ("Nicholas", 29, "software Engineer");
   friend.sayName(); // Nicholas

寄生组织函数运用

在特别状况下为对象建立组织函数。 假定我们想建立一个具有分外要领的特别数组,因为不能直接修正Array组织函数,因而可以运用这个形式。

 function SpecialArray(){
          var values = new Array();

           values.push.apply(values, arguments);

           values.toPipedString = function(){
               return this.join("|");
           }

           return values;
    }

    var colors = new SpecialArray("red", "blue", "green");
    alert(colors.toPipedString());  // red|blue|green
    

寄生组织函数形式:返回的对象与组织函数或许与组织函数的原型属性没有关联;不能依赖于 instanceof操作符肯定对象范例。因而不发起在已运用其他形式的状况下运用该种形式。

稳妥组织函数形式

运用场景

稳妥对象,是指没有大众属性,其要领也不援用this的对象。适合在平安环境下(这些环境会制止运用this 和 new),或许安排数据被其他运用程序修改时运用。

稳妥函数与寄生组织函数差别

  1. 新建立的对象的实例要领不援用this。

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

    function Person(name, age, job) {

       var o = new Object();
       //可以在这里定义私有变量和函数。
       //
       //增加要领
       o.sayName = function(){
           alert(name);
       };
       //返回对象
       return o;

    }

    var friend = Person(“Nicholas”, 29, “software Engineer”);
    friend.sayName();
    以这类形式建立的对象,除了运用sayName()要领之外,没有其他方法接见name的值。

与计生组织函数形式相似,运用稳妥组织函数形式建立的对象与组织函数之间没有什么关联,因而instanceof操作符对这类对象没有意义。

    原文作者:youngfor
    原文地址: https://segmentfault.com/a/1190000006009690
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞