function Foo(name) {
this.name = name
}
Foo.prototype.sayName = ()=> {
console.log(this.name)
}
var f1 = new Foo('f1'); // new 了一个Foo的实例,f1
console.log(f1);
var f2 = new Foo('f2'); // new 了一个Foo的实例,f2
console.log(f2);
console.log(Foo.prototype)
function Parent() {}
Parent.prototype.name="ParentName";
var parent1 = new Parent();
parent1.name = 'parentName';
console.log(parent1.name) // parentName
parent1.name = 'parentName22'; //修改的也是实例上的属性,非原型对象上的属性
console.log(parent1.name) // parentName22
可以用hasOwnProperty判断属性来自实例还是原型
function Parent(){}
Parent.prototype.attr='123';
var p = new parent();
p.name = 'pName';
console.log(p.hasOwnProperty('name')) // true,来自实例。
console.log(p.hasOwnProperty('attr')) // false,来自原型对象
遍历对象属性的方法
for in 可以遍历对象本身和原型上的属性,只要其可枚举。
Object.keys() 只能遍历对象本身的可枚举属性。
Object.getOwnPropertyNames() 遍历实例上所有属性,不管是否可枚举。
new 关键字
new Foo和new Foo(),两种调用方式,除了传参数的区别,看起来做了同样的事?
是做了同样的事,但这里有坑
function Foo() {
return this;
}
Foo.getName = function () {
console.log('1');
};
Foo.prototype.getName = function () {
console.log('2');
};
new Foo.getName(); // -> 1
new Foo().getName(); // -> 2
//new Foo() 的优先级大于 new Foo ,所以对于上述代码来说可以这样划分执行顺序
//new (Foo.getName()); // -> 1
//(new Foo()).getName(); // -> 2
实现一个new
function create() {
// 创建一个空的对象
var obj = new Object(),
// 获得arguments中第一个参数,并在arguments中去除
Con = [].shift.call(arguments);
// 链接到原型,obj 可以访问到构造函数原型中的属性
Object.setPrototypeOf(obj, Con.prototype)
// 绑定 this 实现继承,obj 可以访问到构造函数中的属性
var ret = Con.apply(obj, arguments);
// 优先返回构造函数返回的对象,非对象仍然返回实例,new就是这样
return ret instanceof Object ? ret : obj;
// 如果[[call]]的返回值是对象,那么,返回这个对象,否则返回第一步创建的新对象
};
验证
function Fn(name){
this.name=name
}
Fn.prototype.sayName=function(){console.log(this.name)}
var fn1 = create(Fn,'fn1-name')
完美!
最后,做一道非常全面的题来看看是否理解了new关键字,原型的关系。
function Foo() {
getName = function () {
console.log(1);
}
return this;
}
Foo.getName = function () {
console.log(2);
}
Foo.prototype.getName = function () {
console.log(3);
}
var getName = function () {
console.log(4);
}
function getName() {
console.log(5);
}
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); //2 new (Foo.getName)()
new Foo().getName(); // 3 (new Foo()).getName()
new new Foo().getName(); // 3 new ((new Foo()).getName)()
原型
下图是关于原型,原型链最完整的图,先把它放出来镇楼。。。不着急,下面会慢慢的去理这个图的关系。
要理清那个大图,首先我们要知道构造函数是啥。
下面给一个示例,这个示例在本篇文章中一直用。
下面是打印结果
通过查阅资料,对结果做一下总结:
Foo是一个构造函数,大写首字母是规范,f1,f2 是Foo的实例。
先看f1,f2,都有一个_proto_属性。
所有对象,都有一个隐式引用(非开发者创建)[[prototype]]
chrome浏览器中用_proto_表示这个属性,以下都沿用这一命名。
f1._proto_和 f2._proto_指向一个原型对象,再看Foo,有一个Prototype属性,只要创建新函数,就会给该函数创建这个属性,Foo.prototype也指向一个原型对象,它俩指向同一个原型对象。Foo.prototype上有一个constructor属性,是一个指向prototype所在的函数的指针,这里自然指向了Foo,所有Foo实例都会共享Foo.prototype上的属性和方法。
以上总结建议多读几遍,用心感悟。。。
代码验证一下
这部分对应大图的这一部分。
同理,图中部分也解释的通,只是把我们声明的Foo构造函数换成了原生JS中的Object和Function构造函数。需要注意的点是,Object.prototype._proto_指向了null,可以理解为原型链的终点就是null。
然后,再看Foo.prototype,先前说了它指向原型对象,原型对象也是对象,是对象就有_proto_属性,而且对象都是原生对象Object的实例,所以得出下面结论。
图中这部分也解释通了
在Javascript中,函数也属于对象,不管函数是通过new Function的实例还是function关键字声明的函数,也会有_proto_属性,指向Function.prototype。
解释了下图的红线表记的部分。
至此,大图的所有逻辑都走了一遍。
没搞明白的上滑
原型链
f1,f2 不光可以调用Foo.prototype上的方法,还可以调用Object.prototype上的方法,看图,由_proto_组成的链条就是原型链。
顺便看一个ES5的方法,可以获得,设置对象的原型,代替obj.proto 这种写法,这种写法不规范,有的浏览器不把[[prototype]]用_proto_表示。
规避特性
当获取一个对象上的属性时,会先从此对象上查找,如果没找到,就去原型上找,找到了就返回此属性值,如果原型上有,就返回此属性值,如果没有,就是undefined。
可以用hasOwnProperty判断属性来自实例还是原型
遍历对象属性的方法
for in 可以遍历对象本身和原型上的属性,只要其可枚举。
Object.keys() 只能遍历对象本身的可枚举属性。
Object.getOwnPropertyNames() 遍历实例上所有属性,不管是否可枚举。
new 关键字
new Foo和new Foo(),两种调用方式,除了传参数的区别,看起来做了同样的事?
是做了同样的事,但这里有坑
实现一个new
验证
完美!
最后,做一道非常全面的题来看看是否理解了new关键字,原型的关系。
后面3个都是优先级的问题
解析请看下面的链接,毕竟题都是看的大佬的。。。
http://asyncnode.com/blog/InterviewQuestion/new-javascript.html
这部分写的很糙,是因为《高程》上写的实在是太好了,建议直接看《高程》