在讲原型和原型链之前,我们先来了解一下构造函数。
构造函数
构造函数本身就是一个函数,不过为了规范一般将其函数名的首字母大写。
构造函数和普通函数的区别在于,使用 new 生成实例的函数就是构造函数,直接调用的就是普通函数。
function Person(name) {
this.name = name;
}
let person1 = new Person('xmm');
console.log('姓名:', person1.name);
//姓名: xmm
在上面的代码中,Person就是构造函数,使用new创建了一个实例对象person1。
代码
接下来我会根据一段代码和相应的图解来讲述原型prototype和原型链__proto__的的区别和联系。
//构造函数 Person
function Person(name) {
this.name = name;
}
Person.sex = 'girl';
Person.prototype.age = 18 ;
console.log('Person.sex ',Person.sex); //Person.sex girl
// 构造函数的实例
let person1 = new Person('gxm');
let person2 = new Person('xmm');
console.log('person1.name:', person1.name); //person1.name:gxm
console.log('person2.name:', person2.name); //person2.name:xmm
//修改和自定义属性
person1.age = 20;
person2.loveAction = '指尖的跳跃';
console.log('person1.age:', person1.age); //person1.age:20
console.log('person2.age:', person2.age); //person2.age:18
console.log('person2.loveAction:', person2.loveAction);//person2.loveAction: 指尖的跳跃
console.log('person1.sex', person1.sex); //person1.sex undefined
console.log('person2.sex', person2.sex); //person1.sex undefined
console.log(person1.__proto__ === Person.prototype); // true
console.log(person1.constructor === Person); // true
console.log(Person.prototype.constructor === Person); // true
图解:
原型 prototype
构造函数的原型prototype属性是一个指针,指向的是原型对象,关系如下:
每个函数都会有一个
prototype属性,这个属性是一个指针,指向原型对象(如图中的Person .prototype),这个对象正是调用该构造函数而创建的实例的原型,也就是这个例子中的 person1 和 person2 的原型。
记住只有函数才有,并且通过bind()绑定的也没有。
原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
引申:constuctor和原型对象的关系
现在理解原型对象(Person.prototype)下constructor属性
这个constuctor属性其实就是将原型对象指向关联的构造函数。上述代码中的这两句输出语句可以说明。
console.log(person1.constructor === Person); // true
console.log(Person.prototype.constructor === Person); // true
实例 person1 和 person2可以通过__proto__来访问构造函数的原型对象。就是我们接下来要讲的__proto__属性。
原型链 __proto__
其实每个JS对象都有__ proto __属性,这个属性指向了原型。这个属性在现在来说已经不推荐直接去使用了,这只是浏览器在早期为了让我们访问到内部属性[[prototype]]来实现的一个东西。
在person1和person2实例对象下面有一个[[prototype]]属性,其实没有标准的方式可以访问它,但是主流浏览器上在每个对象上 (null除外) 都支持一个属性,那就是__ proto __,这个属性会指向该对象的原型。
所以总结可得__ proto __就是用来将 实例对象 与 该实例对象的原型相连
new 一个新对象,这个过程发生了什么
- 创建一个新对象
- 原型链继承
- 将构造函数的this指向这个新对象 – call,apply皆可
- 返回新对象
function Person(name, age) {
this.name = name;
this.age = age;
}
console.log(new Person('crystal', 24));
//在这里表示一下new一个新对象的过程
function createPerson(name, age) {
// 1.生成一个新的对象
var obj = Object.create();
// 2.原型链继承,一旦实例化,实例应该拥有原来的函数的原型属性
obj.__proto__ = Person.prototype;
// 3.将Person中的this指向obj
let res = Person.apply(obj, arguments);
// 4.返回在这个res对象
return res instanceOf Object ? res : obj;
}
new Person('crystal', 24) = createPerson('crystal', 24);
优化:
function _new(fn,...arg) {
if (typeof fn !== 'function'){
throw TypeError('the first param must be a function')
}
let obj = Object.create(fn.prototype);
let res = fn.call(obj, ...arg);
let resIsObj = typeof res === 'object' && res !== null;
let resIsFun = typeof res === 'function';
if (resIsObj || resIsFun){
return res
}
return obj;
}
综上所述:
prototype是构造函数访问原型对象,_ proto _是对象实例访问原型对象。
补充:
- Object 是所有对象的爸爸,所有对象都可以通过
__proto__找到Object.prototype,在通过Object.prototype.constructor找到它 - Function 是所有函数的爸爸,所有函数都可以通过
__proto__找到Function.prototype,在通过Function.prototype.constructor找到它 - 函数的
prototype是一个对象 - 对象的
__proto__属性指向原型,__proto__将对象和原型连接起来组成了原型链
番外:
理解一个知识:
Function.prototype.__proto__==Object.prototype;Function.__proto__==Object.__proto__;
这就是为什么:
Function instanceof Object // ->true
Object instanceof Function // ->true
这里来解释一下:
-
先来说说为什么
Object instanceof Function的结果为ture,即Function.prototype在Object的原型链上Function这个内置函数的Function.prototype是一个函数a,因为函数同时也是对象,因此这个函数a也定义了apply、call、bind等属性(或者说方法)。而Object的委托目标Object.__proto__正是这个函数a,因此Object instanceof Function的结果为ture。 -
再来说说为什么
Object.prototype在Function的原型链上,即Function instanceof Object为ture。Object这个内置函数的Object.prototype是一个对象b,很多我们经常用到的属性(或者说方法)如:toString、valueOf、hasOwnProperty、isPrototypeOf就是定义在对象b上的。从图上可以看到,Function的委托目标竟然也是函数a(Function.prototype和Function.__proto__都指向函数a),而函数a的委托目标正是对象b,因此Function instanceof Object为ture。
番外参考的是:blog.csdn.net/haishangfei…