来讲一讲原型链
原型链只存在于函数之中
四个规则
1、引用类型,都具有对象特性,即可自由扩展属性。
2、引用类型,都有一个隐式原型 __proto__ 属性,属性值是一个普通的对象。
3、引用类型,隐式原型 __proto__ 的属性值指向它的构造函数的显式原型 prototype 属性值。
4、当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型 __proto__(也就是它的构造函数的显式原型 prototype)中寻找。
四个知识点
- Object 是所有对象的爸爸,所有对象都可以通过 __proto__ 找到它
- Function 是所有函数的爸爸,所有函数都可以通过 __proto__ 找到它
- 函数的 prototype 是一个对象
- 对象的 __proto__ 属性指向原型, __proto__ 将对象和原型连接起来组成了原型链
const obj = {};
const arr = [];
const fn = function() {}obj.__proto__ == Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ == Function.prototype // true
new 做了什么
var obj = new F();
// 做了什么
var obj = {};
obj.__proto__ = F.prototype;
F.call(obj);
第一行,我们创建了一个空对象obj;
第二行,我们将这个空对象的__proto__成员指向了F函数对象prototype成员对象;
第三行,我们将F函数对象的this指针替换成obj,然后再调用F函数.
我们可以这么理解: 以 new 操作符调用构造函数的时候,函数内部实际上发生以下变化:
1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this.
prototype和__proto__
1. __proto__是每个对象都有的一个属性,而prototype是函数才会有的属性。
2. __proto__指向的是当前对象的原型对象,而prototype指向的,是以当前函数作为构造函数构造出来的对象的原型对象。
你的__proto__来自你构造函数的prototype;所有对象字面量都是通过Object()构造出来的,换言之,对象字面量__proto__ 属性都指向Object.prototype
- prototype: 显式原型
每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象。
通过Function.prototype.bind方法构造出来的函数是个例外,它没有prototype属性
JavaScript中任意对象都有一个内置属性[[prototype]],在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过__proto__来访问。ES5中有了对于这个内置属性标准的Get方法Object.getPrototypeOf().
Object.prototype 这个对象是个例外,它的__proto__值为null
隐式原型指向创建这个对象的函数(constructor)的prototype
作用是什么:显式原型的作用:用来实现基于原型的继承与属性的共享。
- __ proto__: 隐式原型
隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问obj这个对象中的x属性时,如果在obj中找不到,那么就会沿着__proto__依次查找。
__proto__的指向:__proto__的指向到底如何判断呢?根据ECMA定义 'to the value of its constructor’s "prototype" ' ----指向创建这个对象的函数的显式原型。所以关键的点在于找到创建这个对象的构造函数,接下来就来看一下JS中对象被创建的方式,一眼看过去似乎有三种方式:(1)对象字面量的方式 (2)new 的方式 (3)ES5中的Object.create() 但是我认为本质上只有一种方式,也就是通过new来创建。为什么这么说呢,首先字面量的方式是一种为了开发人员更方便创建对象的一个语法糖,本质就是 var o = new Object(); o.xx = xx;o.yy=yy; 再来看看Object.create(),这是ES5中新增的方法,在这之前这被称为原型式继承,
构造函数的prototype和其实例的__proto__是指向同一个地方的,这个地方就叫做原型对象
Function和Object
构造函数的prototype和其实例的__proto__是指向同一个地方的,咱们可以来验证一下
- 函数是Function构造函数的实例
- 对象是Object构造函数的实例
那Function构造函数和Object构造函数他们两个又是谁的实例呢?
- function Object()其实也是个函数,所以他是Function构造函数的实例
- function Function()其实也是个函数,所以他也是Function构造函数的实例,没错,他是他自己本身的实例
console.log(Function.prototype === Object.__proto__) // true
console.log(Function.prototype === Function.__proto__) // true
constructor和prototype是成对的,你指向我,我指向你
function fn() {}console.log(fn.prototype) // {constructor: fn}
console.log(fn.prototype.constructor === fn) // true
原型链
什么是原型链呢?其实俗话说就是:__proto__的路径就叫原型链
原型继承
说到原型,就不得不说补充一下原型继承这个知识点了,原型继承就是,实例可以使用构造函数上的prototype中的方法
instanceof
作用:判断B的prototype是否在A的原型链上
A instanceof B
【JS】图解原型链相关练习题,带你彻底搞懂原型链!!!(这可能是掘金画原型链画的最正的😃) - 掘金
第一题
主要坑是 f的构造函数是 F F的构造函数是Obj,
f的所有方法从以下找
f.__proto__ === F.protyotype F.__proto__ === obj.protyotype obj.proto=== null
F的所有方法从以下找
F.__proto__ === Function.protyotype Function.__proto__ === obj.protyotype obj.proto=== null
var F = function() {};Object.prototype.a = function() {console.log('a');
};Function.prototype.b = function() {console.log('b');
}var f = new F();f.a();
f.b();F.a();
F.b();
第二题
b的所有方法从以下找
b.__proto__ === A.protyotype A.proto=== obj.protyotype obj.proto=== null
c的所有方法从以下找
b.__proto__ === A.protyotype A.proto=== obj.protyotype obj.proto=== null
本体核心是每次new时使用的都是构造函数最新的prototype ,老的构造函数引用老的prototype 并不会被覆盖
var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {n: 2,m: 3
}
var c = new A();console.log(b.n);
console.log(b.m);console.log(c.n);
console.log(c.m);
第三题
函数的构造函数一定是Function 对象的构造函数 可能是obj 也可能是new Function
var foo = {},F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';console.log(foo.a);
console.log(foo.b);console.log(F.a);
console.log(F.b);
第四题
new 的时候会函数内this会重新赋值进行覆盖
function A() {}
function B(a) {this.a = a;
}
function C(a) {if (a) {this.a = a;}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);