javascript 面向对象学习(二)——原型与继承

什么是原型?

首先我们创建一个简单的空对象,再把它打印出来

var example = {}
console.log(example)

结果如下:

{
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

 我们创建的example是个空对象,按理说应该什么属性都没有,却出现了一个__proto__属性,这个属性是从哪里来的呢?实际上,当我们调用 var example = {} 时,相当于调用 var example = new Object(),隐式地继承了 Object.prototype 的属性和方法。Object.prototype 对象是 javascript 的根对象,javascript 的每个对象都是从这个对象来的,下面我们通过构造函数的例子看一下prototype、__proto__到底是什么。

我们来定义一个构造函数

function Person() {}

console.log(Person.prototype)

打印出 Person 的 prototype 属性,结果如下:

{
    constructor: ƒ Person(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

可以看到,这个属性包含了consturcor属性(指向自己)和__proto__属性(指向Object.prototype,即根对象。因为在js里,函数也是对象,继承自根对象)。所有的函数都有这样一个叫做prototype的属性,即原型对象,我们可以在原型对象上定义属性和方法:

Person.prototype.name = 'Jack'
Person.prototype.jump = function() {
    console.log(this.name + '-jump')
}
console.log(Person.prototype)

这时打印出的原型对象为

{
    name: "Jack",
    jump: ƒ (),
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

 name 属性和 jump 方法被加到了这个对象上面。那么,这个原型对象有什么用呢?

我们再用这个构造函数创建一个实例:

function Person() {}
Person.prototype.name = 'Jack'var jack = new Person()
jack.sex = 'male'
console.log(jack)

我们在构造函数原型和实例对象上都加了属性,现在把 jack 打印出来如下:

{
    sex: "male",
    __proto__: {
        name: "Jack",
        constructor: ƒ doSomething(),
        __proto__: {
            constructor: ƒ Object(),
            hasOwnProperty: ƒ hasOwnProperty(),
            isPrototypeOf: ƒ isPrototypeOf(),
            propertyIsEnumerable: ƒ propertyIsEnumerable(),
            toLocaleString: ƒ toLocaleString(),
            toString: ƒ toString(),
            valueOf: ƒ valueOf()
        }
    }

可以看到,jack 有个 __proto__ 属性,这个属性跟它的构造函数 Person 的 prototype 属性内容是一样的,我们打印如下内容:

console.log(jack.name)   // Jack

得到的结果是 “Jack”,在jack的属性里并没有name这一项,name 是定义在Person 的 prototype 属性上的,然而我们却能从jack中访问到,这就是原型的继承作用。我们访问jack.name时,浏览器首先查找jack有没有name属性,有的话直接获取,没有的话会去jack.__proto__属性里查询,再查不到继续在jack.__proto__.proto__里找……由此形成一条链,即原型链。在这里jack.__proto__.__proto__就是Object.prototype。需要注意的是,原型继承的核心是prototype,而不是__proto__,__proto__是浏览器内部属性,只是用来访问原型对象prototype属性的。下面我们来看一下怎么让两个不相关的对象通过原型链实现继承。

// 定义父对象
var father = {
    name: 'Jack'
}
// 定义子对象的构造函数
function Son() {}
// 令子对象的构造函数的原型指向父对象
Son.prototype = father
// 创建子对象实例
var son = new Son()

console.log(son.name) // Jack

核心代码就是标红的那句:令子对象的构造函数的原型对象指向父对象。

总结:在javascript里,每个函数都有叫做原型对象的prototype属性,js的根对象是Object.prototype。通过构造函数创建出的实例能继承构造函数原型对象的属性和方法。

原文地址:https://www.cnblogs.com/zdd2017/p/13050643.html