这篇文章-让你了解-JavaScript-中的原型基础篇图文

4次阅读

共计 3821 个字符,预计需要花费 10 分钟才能阅读完成。

JavaScript – Prototype

JavaScript 是一门动态语言, 你可以在任何时候向对象上添加属性, 如下


    function Student() {
        this.name = 'LeBron James';
        this.gender = 'Male';
    }

    var studObj1 = new Student();
    studObj1.age = 15;
    alert(studObj1.age); // 15

    var studObj2 = new Student();
    alert(studObj2.age); // undefined

正如上面的实例, age 属性附加在 studObj1 实例上. 然而 studObj2 实例没有这个属性, 因为 age 属性只在 studObj1 实例上定义了.

那么, 如果想在后期添加一个属性且能被所有的实例所共享, 该怎么办? 答案这就今天主角 Prototype.

Prototype 是一个对象, 默认情况下与 JavaScript 中的任何一个函数或对象有关, 只是唯一区别在于函数的 prototype 属性是 可访问 可修改 的,而 对象 prototype属性是不可见的.

默认情况下任何一个函数包含 Prototype 对象, 如下图:


prototype 对象是一种特殊类型的可枚举对象, 可以将需要附加属添加到其上,这些属性将在其构造函数的所有实例之间共享。

因此, 把上面的示例中使用函数的 prototype 来添加属性, 以便于所有对象中都可以访问到, 如下:


    function Student() {
        this.name = 'LeBron James';
        this.gender = 'M';
    }

    Student.prototype.age = 15;

    var studObj1 = new Student();
    alert(studObj1.age); // 15

    var studObj2 = new Student();
    alert(studObj2.age); // 15

使用 字面量 或 通过 new 关键字和构造函数 的方式创建的每一个对象都包含 __proto__ 属性, 该属性指向创建此对象的函数的 原型对象.

你可以在谷歌和火狐开发者调试工具中查看该属性(__proto__) , 根据下面的示例:


    function Student() {
        this.name = 'LeBron James';
        this.gender = 'M';
    }

    var studObj = new Student();

    console.log(Student.prototype); // object
    console.log(studObj.prototype); // undefined
    console.log(studObj.__proto__); // object

    console.log(typeof Student.prototype); // object
    console.log(typeof studObj.__proto__); // object

    console.log(Student.prototype === studObj.__proto__); // true

正如上面例子看到, 函数通过 [[函数名称]].prototype 方式访问到原型对象. 但是, 对象 (实例) 并没有暴露出 prototype 属性,而是使用 __proto__ 来访问它.

注意

原型对象是一个特殊类型的枚举对象, 不能通过 for...inforEach 进行迭代.

为什么不可枚举, 聪明肯定会想到 属性描述符 , 下面可以通过Object.prototype 这段代码来验证:

    Object.getOwnPropertyDescriptors(Object.__proto__) 
    //
    {constructor: {value: ƒ, writable: true, enumerable: false, configurable: true}
        hasOwnProperty: {value: ƒ, writable: true, enumerable: false, configurable: true}
        isPrototypeOf: {value: ƒ, writable: true, enumerable: false, configurable: true}
        propertyIsEnumerable: {value: ƒ, writable: true, enumerable: false, configurable: true}
        toLocaleString: {value: ƒ, writable: true, enumerable: false, configurable: true}
        toString: {value: ƒ, writable: true, enumerable: false, configurable: true}
        valueOf: {value: ƒ, writable: true, enumerable: false, configurable: true}
    }

enumerabletrue 表示该属性可枚举, 也就是能通过 for in , forEach, Object.keys 来进行迭代. 注意上例中 enumerable 属性都是 false.

Object 对象的原型

前面提及到, 原型对象在对象中是不可见. 使用 Object.getPrototypeOf(obj) 方法来访问实例的原型对象. (这也是推荐方式, __proto__ 并不是标准属性, 在 IE11 以下其它浏览器中没有实现).


    function Student() {
        this.name = 'LeBron James';
        this.gender = 'M';
    }

    var studObj = new Student();

    Student.prototype.sayHi= function(){alert("Hi");
    };

    var studObj1 = new Student();
    var proto = Object.getPrototypeOf(studObj1);  
    // returns Student's prototype object
                
    alert(proto.constructor); 
    // returns Student function 

Object 原型对象包含如下 属性 方法

属性 描述
constructor 返回创建该实例的构造函数
__proto__ 指向创建该实例的构造函数的原型对象.
方法 描述
hasOwnProperty() 返回一个布尔值,指示对象是否包含指定的属性作为该对象的直接属性,而不是通过原型链继承。
isPrototypeOf() 返回一个布尔值,指示指定的对象是否位于调用此方法的对象的原型链中。
propertyIsEnumerable() 返回一个布尔值,该布尔值指示指定的属性是否可枚举。
toLocaleString() 返回本地格式的字符串.
toString() 返回对象字符串形式.
valueOf() 返回指定对象的原始值.

Chrome 和 Firfox 将对象的原型表示为 __proto__, 而内部引用为 [[Prototype]]. IE 不支持, 只有 IE11 包含它.

修改原型

如上所述, 每个对象都能链接到函数的原型对象. 如果您更改了函数的原型, 则只有新对象将链接到更改后的原型. 所有其他现有对象仍然链接到旧的函数原型. 下面实例来演示这个场景:


    function Student() {
        this.name = 'LeBron James';
        this.gender = 'M';
    }

    Student.prototype.age = 15;

    var studObj1 = new Student();
    alert('studObj1.age =' + studObj1.age); // 15

    var studObj2 = new Student();
    alert('studObj2.age =' + studObj2.age); // 15

    Student.prototype = {age : 20};

    var studObj3 = new Student();
    alert('studObj3.age =' + studObj3.age); // 20

    alert('studObj1.age =' + studObj1.age); // 15
    alert('studObj2.age =' + studObj2.age); // 15

使用原型

原型对象被 JavaScript 引擎用来做两件事:

  • 查找对象的属性和方法
  • 在 JavaScript 中实现继承

    function Student() {
        this.name = 'LeBron James';
        this.gender = 'M';
    }

    Student.prototype.sayHi = function(){alert("Hi");
    };

    var studObj = new Student();
    studObj.toString();

在上面的示例, toString() 方法在 Student 中没有定义, 那么它是如何以及从哪里找到 toString() 的呢?

在这里,原型出现了. 首先, JavaScript 引擎检查 studObj 是否存在 toString 方法?. 如果没有找到, 那么它使用 studObj__proto__ 链接指向 Student 函数 原型对象. 如果它仍然无法找到它那么它会在往上层并检查 Object 函数的原型对象, 因为所有对象都是从 JavaScript 中的 Object 派生的, 并查找 toString() 方法. 因此, 它在 Object 函数的原型对象中找到 toString() 方法,因此我们可以调用 studObj.toString().

查找方式, 如下图所示

上述就是原型基本知识点以及应用.

正文完
 0