函数和对象的关系:
函数是对象的一种
Function instanceof Object // true
但是对象是由函数创建的
// 语法糖写法
var obj = {a:10, b: 20};
var arr = [5, 'x', true];
// 对应的内部转换
var obj = new Object();
obj.a = 10;
obj.b = 30
obj // {a: 10, b: 30}
var arr = new Array();
arr[0] = 5
arr // [5]
typeof Array // “function"
typeof Object // “function"
了解原型:
下面通过一个构造函数的举例子来开始
function Fn(){
this.mame = 'emoji';
this.age = 19;
}
// fn1 :实例对象 Fn() : 构造函数
var fn1 = new Fn();
- 每一个函数都有一个属性
prototype
,通过函数名.prototype
指向的,就是函数的原型
,也叫作函数的显示原型
Fn.prototype
-
prototype
的属性值是一个对象
typeof Fn.prototype // "object"
-
prototype
默认只有一个constructor
属性,指向函数本身,即例子中的Fn
Fn.prototype
▼ {constructor: ƒ}
▶ constructor: ƒ Fn()
▶ __proto__: Object
> Fn.constructor
< ƒ Function() { [native code] }
var arr = new Array();
Array.prototype.constructor == ƒ Array() { [native code] }
- 也可以通过
prototype
为函数增加属性或修改原有属性,属性的值可以是多种类型值
function Fn(){
// 基本属性
this.mame = 'emoji';
this.age = 19;
}
Fn.prototype.year = 1999;
Fn.prototype.getName = function(){
return Fn.name;
}
var fn1 = new Fn();
Fn.prototype
▶ {year: 1999, getName: ƒ, constructor: ƒ}
- 每一个对象都有一个属性
__proto__
指向创建该对象的函数的prototype
(实例对象的隐式原型等于函数的显示原型)
fn1.__proto__
▶ {year: 1999, getName: ƒ, constructor: ƒ}
fn1.__proto__ === Fn.prototype // true
基于上面5点,先看下面例子的输出结果,都为true
,如果理解上面5点,那么对输出结果就不存在任何异议:
fn1.__proto__.__proto__ === Object.prototype
// true
Fn.prototype.__proto__ === Object.prototype
// true
fn1.__proto__.__proto__ === Fn.prototype.__proto__
// true
fn1.__proto__
指向创建fn1
的函数:Fn
的原型Fn.prototype
Fn.prototype
返回的是一个对象,对象的__proto__
又指向创建该对象的函数:Object
的原型Object.prototype
那么按照上面的逻辑,Object.prototype
也是一个对象,遵循每一个对象都有属性__proto__
,Object.prototype
肯定也能指向创建它的函数的原型,但是Object.prototype.__proto__
指向的是个null
那么Fn
也是一个函数Function,他肯定也存在__proto__
,那Fn.__proto__
的原型是什么呢,函数是由什么创建的呢,函数的原型又是什么呢?
// 函数的隐式原型指向创建它的函数的显示原型
Fn.__proto__ // ƒ () { [native code] }
// 函数由Function创建
Fn.__proto__ === Function.prototype
// true
function Fn(){
// 基本属性
this.mame = 'emoji';
this.age = 19;
}
var Fn = new Function('this.mame = 'emoji';this.age = 19;');
Function.prototype.__proto__ === Object.prototype
// true
判断一个引用类型的详细对象类型:instanceof
function Fn(){
// 基本属性
this.mame = 'emoji';
this.age = 19;
}
Fn.prototype.year = 1999;
Fn.prototype.getName = function(){
return Fn.name;
}
var fn1 = new Fn();
// instanceof分为两个参数:A - 对象 B - 函数
fn1 instanceof Fn // true
Fn instanceof Object // true
typeof fn1 // "object"
typeof Fn // "function"
Instanceof
的判断原则是:沿着A
的__proto__
这条线来找,同时沿着B
的prototype
这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true
。如果找到终点还未重合,则返回false
。
内部实现:
// L即fn1 R即Fn
function instance_of(L, R) {
// O为Fn.prototype
var R = R.prototype;
// L为fn1.__proto__
L = L.__proto__;
//执行循环
while (true) {
// 不通过
if (L === null)
return false;
// 判断 L.__proto__ === R.prototype
if (L === R)
return true;
L = L.__proto__;
}
}
通过以上内部实现分析,不难理解
Object instanceof Function // true
// 对象的隐式原型指向创建该对象的函数的显示原型,对象由函数创建。函数的显示原型就是函数本身
Object.__proto__ === Function.prototype
Function instanceof Object // true
Function.__proto__ === Object.prototype
false
// Function.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
true
原型链
function Foo(){}
var f1 = new Foo();
f1.a = 10;
Foo.prototype.a = 100;
Foo.prototype.b = 200;
f1.a // 10
f1.b // 200
Foo.prototype
▶ {a: 100, b: 200, constructor: ƒ}
f1.__proto__
▶ {a: 100, b: 200, constructor: ƒ}
- 为什么
fn1
可以使用Fn
的方法?
fn
是从Fn
函数new
出来的实例对象,Fn
是一个构造函数。(构造函数就像一个面团,实例对象就是用面团捏出来的各种自己想要的样子,他们的共性就是原材料是一样的,存在一种继承关系)这样fn
就能调用Fn.prototype
中的属性和方法- 为什么
fn1
和Fn
都定义了a
属性,不会相互影响,而且a
是10
因为f1
是Foo函数new
出来的对象,a
是f1
对象的基本属性,b
因为f1.__proto__
指向Foo.prototype
,在访问一个对象的属性时,先从对象的基本属性里找如果没有则沿着对象的隐式原型(即创建该对象的函数的显示原型)上去找,这就是原型链
var F = function() {}
Object.prototype.a = function() {};
Function.prototype.b = function() {};
// f能不能访问到a、b方法呢?
var f = new F();
f
的__proto__
指向F.prototype
,F.prototype.__proto__
指向Object.prototype
,所以f
可以取到a
方法, 由于f
的原型链上没经过Function.prototype
,所以取不到b
方法。
由于构造函数F
是由Function
new
出来的,所以F.__proto__
指向Function.prototype
,所以F
函数可以取到b
方法。
具体属性查找分析:
- 先在实例对象
f
的内部属性里找一遍;有就直接用,发现没有想要的属性,跳转第二步; - 在
f
的__proto__
(F.prototype
)中找一遍;有就直接用,发现还是没有并不会放弃,跳转到第三步; - 在
f
的__proto__
指向的原型F.prototype
的__proto__
中找一遍,F.prototype.__proto__ === Object.prototype
,在Object.prototype
中找到属性a,寻找结束; - 在对象
Object.prototyp
中还是找不到,则继续沿着对象Object.prototyp
的__proto__
找,可是前面有说明,Object.prototyp.__proto__
返回的是null
,就说名访问的属性不存在;
搜索轨迹:
f(F) -> f.__proto__(F.prototype) ->F.prototype.__proto__->Object.prototype->Object.prototype.__proto__->null
这种搜索的轨迹,形似一条长链, 又因 prototype
在这个搜索规则中充当链接的作用,于是我们把这种实例与原型的链条称作原型链
。
如何区分一个属性是不是基本属性:hasOwnProperty
基于上面的例子
var item;
for(item in f1){
if(f1.hasOwnProperty(item)){
console.log(item); // a
}
}
for(item in f1){
console.log(item); // a b
}