什么是原型?
遍历对象属性,一般是不会显示这个属性的
并不是说它就是 prototype 这个名字,早期ECMA标准里面是没有规范如何查看这个属性,因此某些浏览器为了方便开发,在浏览器内部给对象都添加了一个属性:proto
由于是浏览器添加的,因此可能会有的浏览器的对象有这个属性:proto,有的浏览器没有。因此为了适配各个浏览器,不建议直接使用这个属性,实际上开发也用的少。
有什么用?
简单地说,就是通过一个函数创建出来的实例,都有一个祖先,并继承这个祖先的属性,且这些祖先的属性是多个实例共享的
let Person = function() {};
// 或
function Person() {}
// 给函数的原型设置对象属性
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
console.log(this.name);
};
// 实例化
let person1 = new Person();
person1.sayName(); // "Nicholas" ,这里先检查person1是否有自己的属性,有就使用;没有就访问它的原型里面找
let person2 = new Person();
person2.sayName(); // "Nicholas" ,这里先检查person1是否有自己的属性,有就使用;没有就访问它的原型里面找
// 相同是因为都是同一个原型,可以理解为都是一个祖先传下来的基因
console.log(person1.sayName == person2.sayName); // true
内存表现:
通过这样的设计,可以把一些通用的属性一级一级继承下去。
就比如 toString() 方法,这个方法是Object对象原型上的方法,因此只要是对象,都可以直接用这个方法
因此,可以给这个原型添加一些公共的属性和方法,让它的后代都能共享这些属性和方法。
但不建议在开发中这样操作,可能会导致某个后代修改了公共的这些属性,导致其他后代出现不可预料的问题。
(大家都有公共的方法eating 和 running)
切记这里用到了this,因此不能用箭头函数
================
函数也有原型 prototype
函数都有一个属性:prototype,默认是个空对象 { },这个属性只有函数有,对象是没有的。
查看一下这个属性的特性,如下,有个constructor(构造器的意思)的属性,值是函数自己(循环引用自己),可以写入,不可以遍历,可以修改和删除
当这个函数被new 后,创建的对象的原型属性 proto,就是这个函数的 prototype 属性
true
这种被new的函数,就叫构造函数。是用来构造对象的函数
修改原型
直接把原型属性,指向另一个对象就可以了
但是这里有个问题,这个新的对象没有constructor这个属性
let f1 = new foo()
console.log(f1) // {}
此时加一个属性即可
function foo(){}
foo.prototype = {
constructor:foo
}
// 但最好通过Object.defineProperty 的方式添加
Object.defineProperty(foo.prototype,"constructor",{
value: foo,
configurable:true,
Enumerable:false, // 不可遍历
Writable:true
})
================
原型链
原型链,就是原型像链条一样连起来引用。当访问对象属性,如果当前这个对象没有这个属性,就会沿着这个链条上的对象,一个一个去找。
如下图,打印 obj.address,本身它自己没有这个属性,因此会默认向它的原型对象里面查找;
它的原型对象没有,就往它的原型对象的原型对象查找;
最终找到顶层,如果还没有,就报undefined,找到就返回;
(上图的 proto 属性,是浏览器为了方便开发添加的,不同浏览器可能实现方法不同,比如可能某些浏览器压根没有这个,因此不建议这样使用)
原型链的顶层对象
NodeJS 最顶层的原型
谷歌浏览器最顶层的原型
这个操作相当于,把内置对象Object的原型,赋值给了所有字面量对象的 proto 属性,也就是最顶层的
var obj = {}
// 相当于 var obj = new Object()
// 然后有 obj.__proto__ = Object.prototype;
里面有什么
这个对象看上去是空的,为什么?实际上里面有很多内置的方法,但是都是设置了不可遍历的,所以才看不见
那要怎么查看?可以通过如下方法
里面有包括toString方法、valueOf方法等等