美文网首页JavaScript 进阶营
React.js学习笔记(13) 面向对象编程 + ( 构造函数

React.js学习笔记(13) 面向对象编程 + ( 构造函数

作者: woow_wu7 | 来源:发表于2017-12-24 23:52 被阅读253次

    (1) 对象

    • 对象是单个实物的抽象。
    • 对象是一个容器,封装了属性(property)和方法(method),属性是对象的状态,方法是对象的行为(完成某种任务)。

    (2) 构造函数

    (一)JavaScript 语言的对象体系,是基于构造函数(constructor)和原型链(prototype)。

    • 所谓”构造函数”,就是专门用来生成对象的函数。
    • 构造函数提供模板,描述对象的基本结构。
    • 一个构造函数,可以生成多个对象,这些对象都有相同的结构。
    (二)构造函数的写法就是一个普通的函数,但是有自己的特征和用法。
    • 为了与普通函数区别,构造函数名字的第一个字母通常大写。
    • 构造函数的特点:
      函数体内部使用了 this 关键字,代表了所要生成的对象实例。
      生成对象的时候,必需用 new 命令,调用构造函数。
    
    
    var Vehicle = function () {   // 构造函数首字母要大写,来区分普通函数
      'use strict';               // 严格模式,保证调用构造函数时,忘记加new命令,就会报错
      this.price = 1000;          // this代表所要生成的实例对象
    };
    var v = new Vehicle();        // new命令,执行构造函数Vehicle,返回一个实例对象,保存在变量v中。
    // var v = new Vehicle;       // new命令本身就可以执行构造函数,所以可以带括号,也可以不带括号。
    v.price // 1000               // v继承了构造函数Vehicle中的price属性
    
    
    
    ------
    
    上面代码中,Vehicle (交通工具的意思) 就是构造函数,它提供模板,用来生成实例对象。
    
    为了与普通函数区别,构造函数名字的第一个字母通常大写。
    
    ------
    
    上面代码通过new命令,让构造函数Vehicle生成一个实例对象,保存在变量v中。
    
    这个新生成的实例对象,从构造函数Vehicle继承了price属性。
    
    new命令执行时,构造函数内部的this,就代表了新生成的实例对象
    
    this.price表示实例对象有一个price属性,值是1000。
    
    
    

    (三)如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。

    • 构造函数:new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。
    • 普通函数:return后跟一个对象,new命令会返回return指定的对象,return后跟的不是对象(如:return 'xxxxx';字符串),new命令返回一个空对象
    
    var Vehicle = function () {
      this.price = 1000;
      return 1000;     // 构造函数内部,return后面如果跟一个对象,就会返回return指定的对象
    };                 // 如果构造函数内部return后不是跟的对象,就会忽略这个return语句,返回this对象。
    
    (new Vehicle()) === 1000
    // false
    
    
    上面代码中,构造函数Vehicle的return语句返回一个数值。
    
    这时,new命令就会忽略这个return语句,返回“构造”后的this对象。
    
    
    
    
    ---------------------------------------------------------------------------------
    
    var Vehicle = function (){
      this.price = 1000;
      return { price: 2000 };
    };
    
    (new Vehicle()).price
    // 2000
    
    
    上面代码中,构造函数Vehicle的return语句,返回的是一个新对象。new命令会返回这个对象,而不是this对象
    
    

    (四)如果对普通函数(内部没有this关键字的函数)使用new命令,如果return后跟的不是一个对象,则会返回一个空对象。
    (return 后跟的是对象,还是会返回return指定的对象)

    
    function getMessage() {
      return 'this is a message';
    }
    
    var msg = new getMessage();   // 普通函数使用new命令,会返回一个空对象
                                  // new命令总是要返回一个对象,实例对象或者return指定的对象
    msg // {}
    typeof msg // "object"
    
    
    上面代码中,getMessage是一个普通函数,返回一个字符串。对它使用new命令,会得到一个空对象。
    
    
    这是因为new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象。
    
    本例中,return语句返回的是字符串,所以new命令就忽略了该语句。
    
    
    构造函数中return的不同情况

    (3) new 命令

    new命令的作用,就是执行构造函数,返回一个实例对象。

    • 使用new命令时,根据需要,构造函数也可以接受参数
    
    var Vehicle = function (p) {       // 构造函数也可以接受参数。
      this.price = p;
    };
    
    var v = new Vehicle(500);
    
    
    
    -----------------------------------------------
    
    
    new命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也可以不带括号。下面两行代码是等价的。
    
    var v = new Vehicle();
    var v = new Vehicle;
    
    
    • 如果忘了使用new命令,直接调用构造函数,构造函数就变成了普通函数,并不会生成实例对象。而且由于后面会说到的原因,this这时代表全局对象,将造成一些意想不到的结果。
    
    var Vehicle = function (){
      this.price = 1000;
    };
    
    var v = Vehicle();
    v.price
    // Uncaught TypeError: Cannot read property 'price' of undefined
    
    price
    // 1000
    
    
    
    上面代码中,调用Vehicle构造函数时,忘了加new命令。
    
    导致,price属性变成了全局变量,而变量v变成了undefined。
    
    
    • 为了保证构造函数必须与new命令一起使用,在构造函数内部使用严格模式,即第一行加上use strict
    
    function Fubar(foo, bar){
      'use strict';         // 严格模式中,函数内部的this不能指向全局对象,默认等于undefined
      this._foo = foo;
      this._bar = bar;
    }
    
    Fubar()
    // TypeError: Cannot set property '_foo' of undefined
    
    
    
    上面代码的Fubar为构造函数,use strict命令保证了该函数在严格模式下运行。
    
    由于在严格模式中,函数内部的this不能指向全局对象,默认等于undefined,
    
    导致不加new调用会报错(JavaScript 不允许对undefined添加属性)。
    
    
    • 如果构造函数内部有return语句,而且return后面跟着一个对象,new命令会返回return语句指定的对象;否则,就会不管return语句,返回this对象。
    • 如果return语句返回的是一个跟this无关的新对象,new命令会返回这个新对象,而不是this对象。这一点需要特别引起注意。
    • 如果对普通函数(内部没有this关键字的函数)使用new命令,return后如果跟的不是对象,则会返回一个空对象。

    (4) new.target

    函数内部可以使用new.target属性。如果当前函数是new命令调用,new.target指向当前函数,否则为undefined。

    (5) Object.create() 创建实例对象

    构造函数作为模板,可以生成实例对象。但是,有时只能拿到实例对象,而该对象根本就不是由构造函数生成的,这时可以使用Object.create()方法,直接以某个实例对象作为模板,生成一个新的实例对象。

    
    var person1 = {
      name: '张三',
      age: 38,
      greeting: function() {
        console.log('Hi! I\'m ' + this.name + '.');
      }
    };
    
    var person2 = Object.create(person1);         // 以person1对象为模板,生成person2 对象,相当于复制
    
    person2.name // 张三
    person2.greeting() // Hi! I'm 张三.
    
    
    
    上面代码中,对象person1是person2的模板,后者继承了前者的属性和方法。
    
    








    prototype 对象

    (1) 构造函数的缺点

    同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费。

    • 下面的例子(重要):
    
    function Cat(name, color) {
      this.name = name;
      this.color = color;
      this.meow = function () {
        console.log('喵喵');
      };
    }
    
    var cat1 = new Cat('大毛', '白色');
    var cat2 = new Cat('二毛', '黑色');
    
    cat1.meow === cat2.meow                
    // false
    
    
    
    上面代码中,cat1和cat2是同一个构造函数的两个实例,它们都具有meow方法。
    
    
    由于meow方法是生成在每个实例对象上面,所以两个实例就生成了两次。( 重要 )
    
    也就是说,每新建一个实例,就会新建一个meow方法。( 重要 )
    
    
    这既没有必要,又浪费系统资源,因为所有meow方法都是同样的行为,完全应该共享。
    
    这个问题的解决方法,就是 JavaScript 的原型对象(prototype)。
    
    
    

    (2) prototype 属性的作用

    JavaScript 的每个对象都继承另一个对象,后者称为“原型”(prototype)对象。

    • 一方面,任何一个对象,都可以充当其他对象的原型;
    • 另一方面,由于原型对象也是对象,所以它也有自己的原型。
    • null也可以充当原型,区别在于它没有自己的原型对象
    • JavaScript 继承机制的设计就是,原型的所有属性和方法,都能被子对象共享。( 重要 )
    • 每一个构造函数都有一个prototype属性,这个属性会在生成实例的时候,成为实例对象的原型对象。( 重要 )
    • 原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。
    • 当实例对象本身没有某个属性或方法的时候,它会到构造函数的prototype属性指向的对象,去寻找该属性或方法。这就是原型对象的特殊之处。
    • 如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
    • 构造函数也是普通的函数, 所以实际上所有函数都有prototype属性。

    总结:
    原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。

    
    function Animal(name) {
      this.name = name;
    }
    
    Animal.prototype.color = 'white';       
    // 每一个构造函数都有prototype属性,在生成实例的时候成为实例对象的原型对象
    
    
    var cat1 = new Animal('大毛');
    var cat2 = new Animal('二毛');
    
    cat1.color // 'white'      // 实例对象继承了原型对象(prototype)的color属性
    cat2.color // 'white'
    
    
    
    上面代码中,构造函数Animal的prototype对象,就是实例对象cat1和cat2的原型对象。
    
    原型对象上添加一个color属性,结果,实例对象都继承了该属性。
    
    

    (3) 原型链

    对象的属性和方法,有可能定义在自身,也有可能定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)。

    • 如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype,即Object构造函数的prototype属性。
    • Object.prototype对象的原型对象是null对象,而null对象没有自己的原型。
    • Object.getPrototypeOf(object)方法返回指定对象的原型
    • “原型链”的作用是,读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的Object.prototype还是找不到,则返回undefined。
    • 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)
    • 性能影响:在原型链寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。
    
    
    Object.getPrototypeOf(Object.prototype)
    // null
    
    
    
    Object.getPrototypeOf(object)方法返回指定对象的原型
    
    

    (4) constructor 属性

    prototype对象有一个constructor属性,默认指向prototype对象所在的构造函数。

    • 由于constructor属性定义在prototype对象上面,意味着可以被所有实例对象继承。
    • constructor属性的作用,是分辨原型对象到底属于哪个构造函数。
    • 有了constructor属性,就可以从实例新建另一个实例。 ( 重要 )
    • 通过name属性,可以从实例得到构造函数的名称。
    
    
    function P() {}
    var p = new P();    
    
    p.constructor                 // 实例对象继承了构造函数的prototype属性的constructor属性
    // function P() {}
    
    p.constructor === P.prototype.constructor    // P.prototype.constructor指向prototype对象的构造函数
    // true                                      // 即 P.prototype.constructor 指向 大P
    
    p.hasOwnProperty('constructor')      // constructor不是实例对象上的属性
    // false
    
    
    
    
    ----------------------------------------------------------------------------
    
    
    function F() {};
    var f = new F();
    
    f.constructor === F // true
    f.constructor === RegExp // false
    
    
    上面代码表示,使用constructor属性,确定实例对象f的构造函数是F,而不是RegExp。
    
    
    
    • 有了constructor属性,就可以从实例新建另一个实例。( 重要 )
    
    function Constr() {}
    var x = new Constr();
    
    var y = new x.constructor();       // x.constructor == Constr(){}  相当于  var y = new Constr();
    y instanceof Constr // true
    
    
    上面代码中,x是构造函数Constr的实例,可以从x.constructor间接调用构造函数。
    
    
    
    
    
    ------------------------------------------------------------------------------------
    
    这使得在实例方法中,调用自身的构造函数成为可能。
    
    
    
    
    • 通过name属性,可以从实例得到构造函数的名称。
    
    function Foo() {}
    var f = new Foo();
    
    
    f.constructor.name // "Foo"
    
    
    
    prototype对象上constructor属性指向prototype所在的构造函数

    (5) instanceof 运算符

    instanceof运算符返回一个布尔值,表示某个对象是否为指定的构造函数的实例。
    ( instance是实例的意思 )

    • instanceof运算符的左边是实例对象,右边是构造函数
    
    var v = new Vehicle();
    
    v instanceof Vehicle          // true            
    
    
    // instanceof 返回布尔值,表示对象是否是构造函数的实例;
    
    








    Object 对象与继承

    (1) Object.getOwnPropertyNames()

    • Object.getOwnPropertyNames方法返回一个数组,成员是对象本身的所有属性的键名,不包含继承的属性键名。
    • Object.getOwnPropertyNames方法返回所有键名。可以枚举的(enumerable)和 不可以枚举的
    • 只获取那些可以枚举的属性,使用Object.keys方法。

    (2) Object.prototype.hasOwnProperty()-----自身属性

    ( in运算符不会区分该属性是对象自身的属性,还是继承的属性 )

    对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。

    • hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法。
    
    var name = {
        '1':10,
        '2':20
    }
    
    name.hasOwnProperty('1');   // true
    
    
    // 因为hasOwnProperty是Object.prototype对象上的属性,所以被所有实例对象继承。
    
    //实例对象可以直接使用hasOwnProperty
    
    
    ----------------------------
    
    Date.hasOwnProperty('length')    // true
    
    Date.hasOwnProperty('toString')  // false
    
    

    (3) in 运算符和 for…in 循环

    • in运算符返回一个布尔值,表示一个对象是否具有某个属性。它不区分该属性是对象自身的属性,还是继承的属性。
    
    'length' in Date // true
    'toString' in Date // true
    
    
    • 为了在for...in循环中获得对象自身的属性,可以采用hasOwnProperty方法判断一下。
    
    for ( var name in object ) {
      if ( object.hasOwnProperty(name) ) {
        /* loop code */
      }
    }
    
    

    相关文章

      网友评论

        本文标题:React.js学习笔记(13) 面向对象编程 + ( 构造函数

        本文链接:https://www.haomeiwen.com/subject/texhgxtx.html