06-原型

原型

06-原型_第1张图片
原型链.png
    function Person(name) {
        this.name = name;
    }
    var p1 = new Person('Leo');
    var obj = new Object();
    var fn = new Function();
// 对象的原型最终指向Object.prototype,Object.prototype的原型指向null
    console.log( obj.__proto__ === Object.prototype ); // true  普通对象的原型
    console.log( Person.prototype.__proto__ === Object.prototype ); // true 自定义构造函数原型的原型
    console.log( Function.prototype.__proto__ === Object.prototype ); // true Function构造函数原型的原型
    console.log( Object.prototype.__proto__ === null ); // true Object构造函数原型的原型
// 实例的原型指向创建该实例的构造函数的原型
    console.log( p1.__proto__ === Person.prototype ); // true 实例对象的原型
// 函数的原型指向Function.prototype
    console.log( fn.__proto__ === Function.prototype ); // true  普通函数的原型
    console.log( Person.__proto__ === Function.prototype ); // true 自定义构造函数的原型
    console.log( Function.__proto__ === Function.prototype ); // true Function构造函数的原型
    console.log( Object.__proto__ === Function.prototype ); // true Object构造函数的原型
// 实例的constructor属性,继承自其原型
    console.log( p1.hasOwnProperty('constructor') ); // false
    console.log( obj.hasOwnProperty('constructor') ); // false
    console.log( fn.hasOwnProperty('constructor') ); // false
// Function.prototype是一个函数
    console.log( typeof Function.prototype ); // function
  • prototype (原型)属性
    每个函数都有一个prototype属性,这个属性指向一个对象,这个对象就是原型对象。
    原型对象的作用就是定义所有实例对象共享的属性和方法。
    实例对象可以看作从原型对象衍生出来的子对象,原型对象上的所有属性和方法,都能被实例对象共享。
    实例对象本身没有某个属性或方法时,会到原型对象去寻找该属性或方法。
    原型对象上的变动会立刻体现在所有实例对象上。
    如果重写整个原型,会切断现有原型与任何之前已存在的对象实例之间的联系,它们引用的依旧是之前的原型。
    function Person(name) {
        this.name = name;
    };
    Person.prototype.sayHi = function () {
        console.log('Hi,'+ this.name);
    }
    var p1 = new Person('Leo');
// 先创建的实例对象,后修改原型,Person.prototype指向被新对象覆盖
    Person.prototype = { // 字面量相当于Object的实例
        constructor: Person, //校正constructor属性的指向,否则指向Object
        getName: function () {
            console.log(this.name);
        }
    }
    var p2 = new Person('Tom');
// 重写原型前创建的实例的原型依旧指向以前的原型
    p1.sayHi(); // Hi,Leo
    p1.getName(); // 报错
// 重写原型后创建的实例的原型依旧指向新的原型
    p2.sayHi(); // 报错
    p2.getName(); // Tom
  • constructor (构造函数)属性
    每个原型对象都会自动获得一个constructor属性,这个属性默认指向prototype属性所在函数(即构造函数)。
    创建了自定义构造函数后,其原型对象默认只会取得constructor属性,其他的方法则从Object继承而来。
    constructor属性定义在原型对象上面,可以被所有实例对象继承。
    constructor属性不是实例对象自身属性,而是继承自原型对象。
    constructor属性的作用,是分辨原型对象到底属于哪个构造函数.
    function Preson() {}
    var p1 = new Preson();
    console.log(p1.constructor); // function P() {}
    console.log(p1.hasOwnProperty('constructor'));// false
    console.log(p1.constructor === Preson); // true
    console.log(p1.constructor === Function); // false

constructor属性是一种原型对象与构造函数的关联关系,修改原型对象时,一般同时校正constructor的指向。

  • [[Prototype]] 内部属性
    当用构造函数创建一个实例对象后,该实例对象包含一个[[Prototype]]内部属性,指向构造函数的原型对象。
    [[Prototype]]存在于实例对象和原型对象之间,而不是实例和构造函数之间。
    [[Prototype]]内部属性通过proto属性来访问。

  • 原型链
    原型对象本身也是对象,也有自己原型对象,所以就形成了一条原型链(prototype chain)。
    所有对象的对象原型终点可以可以上溯到Object.prototype。
    Object.prototype对象的原型对象是没有任何属性和方法的null对象,null对象没有原型对象。
    读取对象的某个属性属性时,先寻找对象本身的属性,如果找不到就在原型链上找,直到Object.prototype。
    如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性.

  • instanceof 运算符
    instanceof返回一个布尔值,表示左边的实例对象是否为右边的构造函数的实例。
    instanceof的实质是检查构造函数的原型对象是否在实例对象的原型链上。
    instanceof对整个原型链上的对象都有效,因此同一个对象,可能会对多个构造函数都返回true。
    JavaScript之中,只要是对象,就有对应的构造函数。因此,instanceof运算符可以判断值的类型。
    instanceof只适用于对象,不适用基本类型的值。

    var d = new Date();
    console.log(d instanceof Date); // true
    console.log(d instanceof Object); // true
// 等同于
    console.log(Date.prototype.isPrototypeOf(d)); // true
  • in 运算符和 for…in 循环
    in运算符返回一个布尔值,表示一个对象是否具有某个属性。它不区分该属性是对象自身的属性,还是继承的属性。
    'length' in Date // true
    'toString' in Date // true

获得对象的所有可枚举属性(不管是自身的还是继承的),可以使用for...in循环。

    for ( var name in object ) {
      if ( object.hasOwnProperty(name) ) {
        /* loop code */
      }
    }
  • Object.prototype.hasOwnProperty()
    对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上。
    hasOwnProperty方法是JavaScript之中唯一一个处理对象属性时,不会遍历原型链的方法。
    Date.hasOwnProperty('length'); // true
    Date.hasOwnProperty('toString'); // false
  • Object.getOwnPropertyNames()
    Object.getOwnPropertyNames方法返回一个数组,成员是对象本身的所有属性的键名,不包含继承的属性键名
    Object.getOwnPropertyNames(String);
// ["length", "name", "arguments", "caller", "prototype", "fromCharCode", "fromCodePoint", "raw"]
  • Object.prototype.isPrototypeOf()
    对象实例isPrototypeOf方法,用来判断一个对象是否是另一个对象的原型。
    只有某个对象处在原型链上,isPrototypeOf都返回true。
    Object.prototype处在原型链顶端,所以对各种实例都返回true,只有继承null的对象除外。
    Object.prototype.isPrototypeOf({});// true
    Object.prototype.isPrototypeOf([]);// true
    Object.prototype.isPrototypeOf(/xyz/);// true
    Person.prototype.isPrototypeOf(p1);// true
    Object.prototype.isPrototypeOf(Object.create(null));// false
  • Object.prototype.proto
    proto方法可以获取和改写某个对象的原型对象。
    proto方法应少用,而是使用标准方法Object.getPrototypeOf()和Object.setPrototypeOf()。

  • Object.getPrototypeOf()
    Object.getPrototypeOf方法接受一个对象,返回该对象的原型,是获取原型对象的标准方法。

// 对象的原型是Object.prototype
    console.log(Object.getPrototypeOf({}) === Object.prototype); // true
// 函数的原型是Function.prototype
    function Person() {};
    console.log(Object.getPrototypeOf(Person) === Function.prototype); // true
// 实例对象p1的原型是创建该实例的构造函数的原型
    var p1 = new Person();
    console.log(Object.getPrototypeOf(p1) === Person.prototype); // true

获取原型对象方法的比较

    // 获取实例对象obj原型的三种方法:
    var obj = new Object();
    obj.__proto__; // 浏览器内部属性,其他环境不可以部署
    obj.constructor.prototype; // 手动修改原型对象时会失效,需要同时修改constructor属性
    Object.getPrototypeOf(obj)// 推荐
  • Object.setPrototypeOf()
    Object.setPrototypeOf方法可以为现有对象设置原型,返回一个新对象。
    该方法接受两个参数,第一个参数是现有对象,第二个参数是原型对象。
    var a = { name: 'Leo'};
    var b = Object.setPrototypeOf({}, a); // b对象本身为空,原型为a
    console.log(a.isPrototypeOf(b)); // true a是b的原型
    console.log(b.hasOwnProperty(name)); // false, b.name不是b自身的属性
    console.log(b.name); // Leo
    // 等价于
    var b = {__proto__: a};
    console.log(b.name); // Leo

new命令通过构造函数新建实例对象,实质是将实例对象的原型,指向构造函数的prototype属性,然后在实例对象上执行构造函数。

    function Person(name, age) {
        this.name = name;
        this.age = age;
    };
    var p1 = new Person('Leo'); // 使用new新建构造函数
// 等价于
    var p1 = Object.setPrototypeOf({}, Person.prototype);// 将实例对象的原型指向构造函数的prototype属性
    Person.call(p1, 'Leo', 26);// 在实例对象上执行构造函数,并传入参数
    console.log(p1.name); // Leo
    console.log(p1.age); // 26
  • Object.create()
    Object.create方法用于从原型对象生成新的实例对象,可以替代new命令。
    该方法接受一个对象为参数,返回一个新的对象,传入的对象成为新对象的原型。必须提供原有对象,否则报错。
    var A = {
        sayHi: function () {
            console.log('hello');
        }
    };
    var B = Object.create(A); // 在A的基础上生成B,此时A就成为B的原型
    B.sayHi(); // hello B继承了A的所有属性和方法
    // 等同于
    var A = function () {};
    A.prototype.sayHi = function () {
        console.log('hello');
    }
    B = new A();
    B.sayHi() // hello

Object.create方法实质是新建一个构造函数F,让F的prototype属性指向作为原型的对象o,最后返回一个F的实例。

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

下面三种生成新对象的方式是等价的。

var o1 = Object.create({});
var o2 = Object.create(Object.prototype);
var o3 = new Object();

Object.create方法生成的新对象,动态的继承了原型。在原型上的修改,会立刻反映在新对象上。

var A = {x: 1};
var B = Object.create(A);
A.x = 2;
console.log(B.x); // 2

Object.create方法还可以接受第二个参数,该参数是一个属性描述对象,它所描述的对象属性,会添加到新对象。
Object.create方法生成的对象,继承了他的原型对象的构造函数。

function A() {}
var a = new A();
var b = Object.create(a); // b对象的原型是a,因此继承了a的构造函数A
console.log(b.constructor === A); // true
console.log(b instanceof A); // true

参考资料:
《JavaScript高级程序设计》
《JavaScript 标准参考教程》

你可能感兴趣的:(06-原型)