Appearance
原型和原型链
之所以存在原型,是因为JS语言要实现面向对象,而原型是面向对象的实现手段之一。能否判断一个实例的类型
成为了一个语言能否支持面向对象的标准。JS通过原型的方式避免了类型的丢失,通过原型可以知道某个对象属于哪个类型。
在 JavaScript 中,每一个引用数据类型都有一个内部属性 _proto_
,该属性指向另外一个对象,该对象被称之为原型(prototype)。
js
let arr = [];
let obj = {};
let func = function() {};
arr.__proto__ === Array.prototype; // true
obj.__proto__ === Object.prototype; // true
func.__proto__ === Function.prototype; // true
在这些引用数据类型的原型上,定义了一些公用的变量和方法。当我们定义对应类型的变量时,就可以通过原型链的方式继承到这些公用的属性和方法。
构造函数
原型(prototype)通常在构造函数中定义:
js
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function() {
console.log('hi!, i am ' + this.name);
}
let ming = new Person('小明', 18)
ming.say(); // hi!, i am 小明
如上所示,定义了一个名为Person
的构造函数,在构造函数的原型上定义了say
方法。通过Person
构造函数创建出来的实例对象中,其原型上都会存在say
方法。当执行ming.say()
时,会现在ming实例对象中查找是否存在say
方法,如果没有找到,则通过原型链的方式找到这个对象的原型,从而执行原型上的say
方法。
js
ming.valueOf(); // Person {name: '小明', age: 18}
如上所示,在ming这个实例对象和它的原型上,都没有定义valueOf方法,为什么调用了valueOf方法没有报错呢?继续往下探究。
前面说到,每一个引用数据类型都有一个内部属性 _proto_,该属性指向另外一个对象,该对象被称之为原型。
而ming这个实例对象的原型,本身也是一个对象,它也有__proto__
属性,所以它也会有自己的原型,也就是Object这个构造函数的原型。而Object构造函数的原型上定义了我们常见的对象处理方法,例如hasOwnProperty
/isPrototypeOf
/toString
等等,当然也包括例子中的valueOf
。
实际上,还是通过原型链的方式,找到了Object的原型,从而调用了对应的方法。
需要注意的是,Object构造函数的原型也是对象,也存在__proto__
属性,但是它为null
,原型链的顶端就到null
为止。
总结
综上所述,我认为原型就是一个用来存放公用的变量和方法的对象。通过原型链的方式可以继承一些公用的变量和方法,从而让代码更加的清晰和简洁,每一个对象或方法可以更专注于本身所需要处理的业务逻辑。