首先做到题看看对原型和原型链的理解如何
//第一题
var F = function () {}
Object.prototype.a = function () {}
Function.prototype.b = function () {}
var f = new F();
console.log(f.a, f.b, F.a, F.b);
//第二题
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);
在这里就不揭晓答案了,否则一个不注意看到了答案就测试不了自己了
这都是很简单的题了,如果做错了,或者做的过程中磕磕碰碰,可要好好看看下面的讲解咯
最后还有一道压轴题,我相信看完之后可以很轻松做出来
原型和原型链
- 所有对象都是通过
new 函数
创建 - 所有的函数也是对象
- 函数中可以有属性,比如Array.from()、Number.isNaN()
- 所有对象都是引用类型
原型 prototype
所有函数都有一个属性:prototype,称之为函数原型
默认情况下,prototype是一个普通的Object对象
默认情况下,prototype中有一个属性,constructor,它也是一个对象,它指向构造函数本身。
隐式原型 proto
所有的对象都有一个属性:__proto__
,称之为隐式原型,因为函数也是对象,所以函数也拥有__proto__
默认情况下,隐式原型指向创建该对象的函数的原型。
function test(){
}
var obj = new test();
console.log(obj.__proto__ === test.prototype); //true;
请看下面两道面试题:
function A() {}
function B() {}
function create() {
if (Math.random() < 0.5) {
return new A();
} else {
return new B();
}
}
var obj = create();
//如何得到创建obj的构造函数名称
console.log(obj.__proto__.constructor.name);
function A() {}
var obj1 = new A();
var obj2 = new A();
obj1.abc = 123;
obj2.__proto__.bcd = 456;
//输出结果是多少
console.log(obj1.abc, obj2.abc); //123 undefined
console.log(obj1.__proto__.bcd, obj2.__proto__.bcd);//456 456
第二个输出都是456的原因就是,obj2.proto.bcd = 456;就相当于给A的原型链上添加了一个属性bcd:A.prototype.bcd = 456
所以A的所有对象都有了一个bcd属性
当访问一个对象的成员时:
- 看该对象自身是否拥有该成员,如果有直接使用
- 在原型链中依次查找是否拥有该成员,如果有直接使用
//函数功能:给字符串的首字母大写
String.prototype.camel = function() {
return this.replace(/\b(\w)(\w*)\b/g, function($, $1, $2) {
return $1.toUpperCase() + $2;
}).replace(/\s/g, "");
}
上面这种给对象原型添加属性或者方法的方式,有许多叫法,最贴切的应该是猴子补丁
猴子补丁:在函数原型中加入成员,以增强起对象的功能,猴子补丁会导致原型污染,使用需谨慎。
原型链
记住了这张图,我想应该可以应付大多数关于原型链的题目了
特殊点:
- Function的__proto__指向自身的prototype
- Object的prototype的__proto__指向null
//第一种方法
function A() {}
var obj = new A();
obj.toString = function() {
return "123";
}
//第二种方法
function A() {}
A.prototype.toString = function() {
return "123";
}
var obj = new A();
此时在调用obj.toString()得到的结果就是’123’,产生这种情况的原因就是,当obj调用toString时
在自己身上就找到了需要的东西,就不会再向上找。
第二种方法会把toString方法添加在函数的原型上,被所有的对象共享,只要是通过这个函数创建的对象都会共享这个函数
如果想强行调用Object的toString可以这样做:
Object.prototype.toString.call(obj);
来看最后一道题目,可能比较难,不过,只要记住了上面的图,这都不是事
function User() {}
User.prototype.sayHello = function() {}
var u1 = new User();
var u2 = new User();
console.log(u1.sayHello === u2.sayHello);
console.log(User.prototype.constructor);
console.log(User.prototype === Function.prototype);
console.log(User.__proto__ === Function.prototype);
console.log(User.__proto__ === Function.__proto__);
console.log(u1.__proto__ === u2.__proto__);
console.log(u1.__proto__ === User.__proto__);
console.log(Function.__proto__ === Object.__proto__);
console.log(Function.prototype.__proto__ === Object.prototype.__proto__)
console.log(Function.prototype.__proto__ === Object.prototype);
原型链的应用
基础方法
W3C不推荐直接使用系统成员__proto__
Object.getPrototypeOf(对象)
获取对象的隐式原型
Object.getPrototypeOf === obj.proto
Object.prototype.isPrototypeOf(对象)
判断当前对象(this)是否在指定对象的原型链上
function A() {};
var obj = new A();
var o = {};
o.isPrototypeOf(obj);//false
//换种写法
Object.getPrototypeOf(o).isPrototypeOf(obj); //true
因此o的隐式原型是Object.proto,正好它也在obj的原型链上,所以是true,具体可以看上图
对象 instanceof 函数
判断函数的原型是否在对象的原型链上
Object.create(对象)
创建一个新对象,其隐式原型指向指定的对象
Object.prototype.hasOwnProperty(属性名)
判断一个对象自身是否拥有某个属性,在循环遍历的时候经常使用
应用
类数组转换为真数组
Array.prototype.slice.call(类数组);
这个方法与[].slice.call(类数组)的区别:后者的[]是重新创建了一个数组从而得到slice方法,但是这是不必要的
创作不易,转载请注明出处