zoukankan      html  css  js  c++  java
  • JavaScript基础概念之----面向对象----创建对象(深入原型及原型链)

    一、使用Object构造函数或对象字面量创建对象

    var person = new Object()
    person.name = 'adhehe';
    person.age = 23;
    //...
    // 
    var person = {
      name:'adhehe',
      age:23,
      //...

    }

    缺点:使用同一个接口创建很多的对象,会产生大量的重复代码。

    二、工厂模式

    这种模式抽象了创建具体对象的过程。用函数来封装以特定接口创建对象的细节。

    function createPerson(name,age){
      //将创建的具体细节封装起来,最后返回一个对象
    var o = new Object(); o.name = name; o.age = age; o.getName = function(){ return this.name; } return o; } var person1 = createPerson('adhehe',23); var person2 = createPerson('Jhone',45);

    缺点:虽然解决了创建多个相似对象的问题,但没有解决 对象识别的问题,即怎么知道一个对象的类型

    三、构造函数模式

    构造函数可以用来创建特定类型的对象。如Object、Array等。也可以创建自定义构造函数。

    function Person(name,age){
        this.name = name;
        this.age = age;
        this.getName = function(){
            return this.name;
        }
    }    
    
    var person1 = new Person('adhehe',23);
    var person2 = new Person('Jhone',45);

    以new关键字创建Person的实例,经历以下4个步骤:

    • 创建一个新对象
    • 将构造函数的作用域赋给了新对象(即this指向了这个新对象)
    • 执行 构造函数中的代码(即为新对象添加属性)
    • 返回新对象

    instanceof 操作符 可以检测对象的类型

    //以person为例
    
    console.log(person1 instanceof Person)
    console.log(person2 instanceof Person)
    console.log(person1 instanceof Object)
    console.log(person2 instanceof Object)
    //都输出 true

    构造函数与普通函数的唯一区别是:调用的方式不同。只要通过new操作符调用,它就是构造函数。

    缺点:每个方法都要在每个实例上重新创建一遍。

    //以上面 person为例
    
    person1.getName == preson2.getName
    //false

    四、原型模式

    创建的每个函数都有一个prototype(原型)属性,它是一个指针,批向一个对象,而这个对象的用途就是:包含可以共享的属性和方法,给那些特定类型的所有实例。

    function Person(){}
    
    Person.prototype.name = 'adhehe'
    Person.prototype.age = 23
    Person.prototype.getName = function(){
      return this.name;
    }
    
    var preson1 = new Person()
    var preson2 = new Person()
    
    console.log(person1.getName())
    console.log(person2.getName())
    //都返回 adhehe
    
    person1.getName == person2.getName
    //true

     1、理解原型对象

    只要创建了函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象。

    所有原型对象都会自动获得一个constructor属性(构造函数),这个属性指向prototype属性所在的函数的指针。

    当调用构造函数创建一个实例后,实例内部将包含一个指针(__ptoto__),指向构造函数的原型对象。

    Person.prototype.constructor == Person

    person1.constructor == Person
    person2.constructor == Person

    person1.__proto__ == Person.prototype
    person2.__proto__ == Person.prototype

    isPrototypeOf()方法 确定对象之间是否存在原型关系

    Person.prototype.ifPrototypeOf(person1) //true
    Person.prototype.ifPrototypeOf(person2) //true

    Object.getPrototypeOf()方法 是ECMAScript5新增的检测方法

    Object.getPrototypeOf(person1) == Person.prototype //true
    Object.getPrototypeOf(person2) == Person.prototype //true

    每当读取某个对象中的某个属性时,首先搜索该对象本身,再搜索该对象指向的原型对象。如果在实例对象中某个属性名与原型中的一个属性同名,该属性将会屏蔽原型中的属性。

    function Person(){}
    
    Person.prototype.same = 1;
    
    var person1 = new Person()
    var person2 = new Person()
    person1.same
    = 2; console.log(person1.same) //2 console.log(person2.same) //1

    使用delete操作符 可以完全删除实例属性,从而能够重新访问原型中的属性。

    //接上例
    
    delete person1.same;
    console.log(person1.same) //1

    hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。

    person1.hasOwnProperty('name') // true
    person1.hasOwnProperty('hello') //false

    2、in 操作符

    有两种方式使用:

    • 单独使用
    • for-in

    单独使用:通过对象能够访问指定属性时返回true,无论该属性存在于实例还是原型中。

    "name" in person1 //true

    同时使用 hasOwnProperty()方法和 in 操作符,就可以确认该属性到底是存在于对象中,还是存在于原型中。

    function has(obj,name){
        return !obj.hasOwnProperty(name) && (name in obj);
    }
    
    //in为true,hasOwnProperty()返回false,就可以确定属性是在原型中

    for-in 循环:返回所有能够通过对象访问的、可枚举的属性,其中既包括存在于实例中的属性,也包括存在于原型中的属性。屏蔽了原型中不可枚举的属性的实例属性也会在for-in循环中返回。

    Object.keys()方法 可以取得对象上所有可枚举的实例属性。

    function Person(){}
    
    Person.prototype.name = 'adhehe'
    Person.prototype.age = 23
    Person.prototype.getName = function(){
      return this.name;
    }
    
    var keys = Object.keys(Person.prototype)
    console.log(keys) // "name,age,getName"
    
    var person1 = new Person()
    person1.name = 'Rob'
    
    var person1Keys = Object.keys(person1)
    console.log(person1Keys) //"name"

    Object.getOwnPropertyNames()方法 可以得到所有实例属性,无论它是否可枚举。

    var keys = Object.getOwnPropertyNames(Person.prototype);
    console.log(keys)
    //"constructor,name,age,getName"

    3、更简单的原型语法

    function Person(){}
    
    Person.prototype = {
        name:'adhehe',
        age:23,
        getName:function(){
            return this.name
        }
    }

    在这里,constructor属性不再指向Person。可以 将constructor属性值设置回适当的值

    function Person(){}
    
    Person.prototype = {
        constructor:Person,
        name:'adhehe',
        age:23,
        getName:function(){
            return this.name
        }
    }

    以上面这种方式重设constructor属性会导致它的Enumerable特性被设置为true。默认情况下原生的constructor是不可枚举的。可以使用Object.defineProperty()修改

    function Person(){}
    
    Person.prototype = {
        name:'adhehe',
        age:23,
        getName:function(){
            return this.name
        }
    }
    
    Object.defineProperty(Person.prototype,'constructor',{
        enumberable:false,
        value:Person
    })

    4、原型的动态性

    对原型对象的修改都能够立即 从实例上反映出来。可以随时为原型添加属性和方法。

    var o = new Person();
    
    Person.prototype.sayHi = function(){
        //...
    }
    
    o.sayHi()  //没有问题

    但是,如果重写原型对象,则会切断 构造函数与最初原型之间的联系。实例中的指针仅指向原型,而不指向构造函数。

    function Person(){}
    
    var o = new Person();
    
    Person.prototype = {
        constructor:Person,
        name:'adhehe',
        age:23,
        getName:function(){
            //....
        }
    }
    
    o.getName() // 报错

    5、原生对象的原型

    所有原生的引用类型,都在其构造函数的原型上定义了方法。

    我们也可以在原生对象的原型上定义新的方法或修改原型,但不推荐这么做。

    缺点:

    • 省略了为构造函数传递初始化参数的环节,导致所有的实例在默认情况下都将取到相同的属性值
    • 由于原型的共享本性的原因,在其中一个实例对象上添加或修改了原型属性的值,其他实例对象都会被修改
    function Person(){}
    
    Person.prototype = {   
        constructor:Person,
        name:'adhehe',
        age:23,
        friends:['a','b'],
        getName:function(){
            //...
        }
    }
    
    var o1 = new Person()
    var o2 = new Person()
    
    o1.friends.push('c')
    
    console.log(o1.friends) //a,b,c
    console.log(o2.friends) //a,b,c
    //两个对象实例的friends返回了相同的值

    五、构造函数模式 与 原型模式 组合

    • 构造函数模式用于定义实例属性
    • 原型模式用于定义方法和共享的属性
    function Person(name,age){
        this.name = name;
        this.age = age;
        this.friends = ['a','b']
    }
    
    Person.prototype = {
        constructor:Person,
        getName:function(){
            //...
        }
    }
    
    var o1 = new Person('adhehe',23);
    var o2 = new Person('Greg',45);
    
    o1.friends.push('c');
    
    console.log(o1.friends)  //a,b,c
    console.log(o2.friends)  //a,b
    console.log(o1.friends === o2.friends)  //false
    console.log(o1.getName === o2.getName)  //true

    六、动态原型模式

    在构造函数中初始化原型(仅在必要的情况下),它保持了同时使用构造函数和原型的优点。

    它只会在初次调用构造函数时才会执行,此后,原型已经完成初始化,不需要再做什么修改。

    使用动态原型模式时,不能使用对象字面量重写原型。会切断现有实例与新原型之间的联系。

    function Person(name,age){
        this.name = name;
        this.age = age;
    
        if(typeof this.getName != 'function'){
            Person.prototype.getName = function(){
                //...
            }
        }
    }
    
    var o = new Person('adhehe',23)
    o.getName()

    七、寄生构造函数模式

    创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

    这个模式与工厂模式是一模一样的,不同的是这里使用 new 操作符创建实例对象,并把包装函数叫作构造函数。

    function Person(name,age){
        var o = new Object();
        o.name = name;
        o.age = age;
        o.getName = function(){
            //...
        }
    
        return o;
    }
    
    var person1 = new Person('adhehe',23)
    person1.getName();

    缺点:返回的对象与构造函数或构造函数的原型属性之间没有任何关系,所以不能依赖 instanceof 操作符来确定对象类型。在有其他模式可用的情况下,不推荐使用这种模式

    八、稳妥构造函数模式

    稳妥对象:指的是没有公共属性,其方法也不引用 this 的对象。

    稳妥对象 最适合在一些安全的环境中,或者在防止数据被其他应用程序改动时使用。

    此模式 与 寄生构造函数类似的模式,有两点不同:

    • 新创建对象的实例方法不引用 this
    • 不使用 new 操作符调用构造函数
    function Person(name,age){
        var o = new Object();
    
        //在这里定义私有变量与属性
    
        o.getName = function(){
            return name;
        }
    
       return o;
    }
    
    var person1 = Person('adhehe',23)
    person1.getName() //adhehe
    
    //在这种模式创建的对象中,除了使用getName()方法之外,没有其他办法可以访问name

    缺点:与寄生构造函数模式类似,返回的对象与构造函数或构造函数的原型属性之间没有任何关系,所以不能依赖 instanceof 操作符来确定对象类型。

  • 相关阅读:
    SQL Server 数据库基础编程
    Spring整合DWR comet 实现无刷新 多人聊天室
    SQL Server 数据库安全
    【MongoDB for Java】Java操作MongoDB
    SQL Server 触发器
    使用Dom4j操作XML
    BlazeDS 整合 Flex HelloWorld 示例
    SQL Server 索引和视图
    JDOM 操作XML
    SQL Server 数据库设计
  • 原文地址:https://www.cnblogs.com/adhehe/p/9787725.html
Copyright © 2011-2022 走看看