js对象
js中的类型有:
number,string,boolean,object,undefined,function
所以对象可以这么分类:
- 函数对象(function)
- 其他对象
所以函数对象可以大声唱我们不一样,既然不一样肯定得有一点不一样的烟火,函数对象的不一样在什么地方呢? 每一个函数对象都有一个prototype属性(Function.prototype除外)
作为一个傻傻的后端的我一直以为每一个对象都有一个prototype属性。每一个对象都有的是一个叫做__proto__的隐藏属性,它指向的就是对象所对应的函数对象的prototype(也就是传说中的原型对象)
后面我们再举例子来说明原型对象,我们先看一下那些是函数对象,方便理解。
那些是函数对象
function FN() {}
var FA = function() {}
var FF = new Function('arg1', 'arg2', 'console.log(arg1 + arg2)');
FF('a','b');
console.log('typeof Object:' + typeof Object); //function
console.log('typeof Object.prototype:' + typeof Object.prototype); //object
console.log('typeof Function:' + typeof Function); //function
console.log('typeof Function.prototype:' + typeof Function.prototype); //function
console.log('typeof FN:' + typeof FN); //function
console.log('typeof FA:' + typeof FA); //function
console.log('typeof FF:' + typeof FF); //function
不那么精确的总结一下就是:
- 带function关键字的
- new Funtion出来的对象
- Funtion,Object,Function.prototype对象
这里说一下Function.prototype,因为它又是一个不一样的烟火。因为一般的函数对象的prototype都是普通对象,但是Function.prototype是一个函数对象,并且这个函数对象没有prototype属性。
所有的函数对象的__proto__都指向的是Function.prototype
可以使用:
console.log(Object.getOwnPropertyNames(Function.prototype))
打印一下Function.prototype的属性,会发现都是一下公共函数。
原型链
我们把问题简化来处理,看一个最常见的简化的原型链:
function Person(name, age) {
this.name = name;
this.age = age;
this.hobby = function() {
return '实例对象hobby';
}
}
Person.hobby = function() {
return '函数对象hobby';
}
Person.prototype.hobby = function(){
return 'prototype hobby';
}
var p1 = new Person("tim",20);
var p2 = new Person("alice",20);
console.log(p1.hobby === p2.hobby);//false
console.log(p1.__proto__ == Person.prototype);//true
console.log(p2.__proto__ == Person.prototype);//true
console.log(p1.__proto__ == p2.__proto__);//true
console.log(p1.__proto__.hobby == p2.__proto__.hobby);//true
console.log(p1.hobby());//实例对象hobby
console.log(Person.hobby());//函数对象hobby
从前面介绍的我们知道Person是一个函数对象,既然是一个函数对象,那么Person是有prototype属性的,所以给Person的prototype的属性添加一个hobby属性也是合理的事情了。
Person是一个函数对象,那么说Person是一个对象也是合理的了,给Person添加一个hobby属性也是合理的了。
下面就要看一下关键的new关键字的行为了,我们知道Person是一个函数对象,new一个函数对象,基本上执行的就是属性拷贝,然后把new出来的对象的__proto__指向函数对象的prototype的属性。
所以上面的p1.hobby === p2.hobby是false是因为他们是2个对象中不同的属性,是2个不同的引用。
而p1.proto.hobby == p2.proto.hobby是true是因为这2个都是Person.prototype对象上的属性hobby是同一个引用。
js原型链查找方式就是在当前对象中查找这个属性,如果没有就在__proto__指向的对象中去查找,依次查找到Object.prototype结束,因为Object.prototype.proto == null
下面思考一个问题:如果把上面的:
Person.hobby = function() {
return '函数对象hobby';
}
去掉会有问题吗?
答案是会有问题,这个问题有点绕,但是记住我们前面总结的,所有的函数对象的__proto__指向的都是Function.prototype
所有如果没有上面的代码Person.hobby()这个调用在Person函数对象中找不到hobby属性,就会在Funtion.prototype对象中找,依次找到Object.prototype,显然是找不到的。
注意原型链,不是在Person.prototype中查找,那不在Person对象的原型链。