原型与原型链

原型对象

构造函数可以通过new的方法来创建一个实例,而每一个构造函数都有一个prototype的属性指向一个原型对象。原型对象中所有的属性与方法都可以被该函数所创建的实例所共享。

原型链

每个被构造函数创建出来的实例都有一个隐式的属性__proto__,该属性指向构造函数的prototype,即原型对象。浏览器在取对象中的方法时,会先在对象中寻找是否存在该方法,如果没有则通过__proto__到其原型对象上寻找,以此一层层通过__proto__,直至为null。

原型链是基于proto形成的,继承是通过prototype实现的

Object,Function,__proto__,prototype

在JS中,万物皆对象,所以函数(方法)也是一个对象。

所有的对象都有一个根源 Object.prototype,Object.prototype只是一个普通对象

根源的原型对象是null

但是null却不是一个对象,虽然用typeof验证null为object,这个是JS的bug。

(typeof Object.prototype) === object;//true
Object.prototype.__proto__=== null;//true
Object.prototype.prototype === undefied;//true

特例:__Object和Function既是对象,又是函数,两者内部同时含有proto和prototype属性,他们关系较为复杂__Function.prototype指向“__内置函数__”,而Object.prototype指向“__根源对象__”

Object.__proto__ === Function.prototype //true
Object.__proto__ === Function.__proto__//true
Object.prototype === Function.prototype.__proto__ // true
//因此
Function instanceof Object //true
Object instanceof Function //true

x instanceof y,当y的原型对象在x的原型链之上,返回true,否则返回false

构造函数与普通函数

创建方式相同

构造函数也是个普通函数,但是一般来说,构造函数会采用首字母大写的方式。

调用方式不同,目的不同

普通函数: 直接调用,返回值为函数调用的之后return的值,无return则为空;
构造函数:需要用new调用,以创建实例的方式来调用,目的是创建一个指向构造函数的原型的一个实例对象。

构造函数调用创建实例的时候内含多步骤

这个问题可以换个角度说,就是当我们再new一个对象的时候,内部发生了什么。
1、首先需要创建一个空对象。
2、将空对象的__proto__指向构造函数的prototype(构造函数的原型对象),以此获得原型的方法与属性
3、绑定this的指向
4、返回新对象
在文章下方会手动实现一个简易版的new方法

构造函数用this构造内部方法与属性

function Cat(name, age) {
    this.age = age
    this.name = name
    this.run = run
    function run() {
        
    }
}
let cat = new Cat('米粒', '6个月')

构造函数所创建的实例都可以用instanceof

可以用来验证该实例是否由验证的构造函数所创建的。原理就是去查找实例的__proto__是否与构造函数的prototype所指向的原型对象是同一个。
文章下方将会手写一个简略版的instanceof

手写new

当我们再new一个对象的时候,内部发生了什么。

1、首先需要创建一个空对象。
2、将空对象的__proto__指向构造函数的prototype(构造函数的原型对象),以此获得原型的方法与属性
3、绑定this的指向
4、返回新对象

function Cat(name, age) {
    this.age = age
    this.name = name
    this.run = run
    function run() {
        
    }
}

function _new() {
    // 
    let newObject = new Object()
    // arguments是传入函数的参数,因为是类数组的形式,无法直接使用shift方法
    // 所以使用ES6的新特性进行转换
    // Array.prototype.shift.apply(arguments)用数组原生方法也是可以的
    arguments = Array.from(arguments)
    // 取出构造函数
    let Constructor =  arguments.shift()
    //  获得构造函数的原型对象上的方法与属性
    newObject.__proto__ = Constructor.prototype
    // 绑定this的指向
    Constructor.apply(newObject, arguments)
    return newObject
}

cat = _new(Cat, '米粒', '6个月')
console.log(cat)
// Cat { age: '6个月', name: '米粒', run: [Function: run] }

image.png

手写instanceof

原理:查找实例的__proto__是否与构造函数的prototype所指向的原型对象是同一个。

function instance_of(L, R) {
    const R_prototype = R.prototype
    let L_proto = L.__proto__
    let flage = false
    while(L_proto !== null) {
        if (L_proto === R_prototype) {
            flage = true
            break
        }
        L_proto = L_proto.__proto__; 
    }
    return flage
}

// 使用上方的_new方法来创建实例
cat = _new(Cat, '米粒', '6个月')
console.log(cat)

console.log(cat instanceof Cat) // true
console.log(instance_of(cat, Cat)) // true
console.log(instance_of(cat, Object)) // true
console.log(instance_of(cat, {})) // false
console.log(instance_of(cat, Boolean)) // false

image.png


老虎不长牙
128 声望6 粉丝

« 上一篇
手写简单Vue