原型链

常见概念

  • 构造函数

  • 构造函数-扩展

  • 原型规则和示例

  • 原型链

  • instanceof

构造函数

任何一个函数都可以被new,new了之后,就成了构造方法。

如下:

function Foo(name, age) {

this.name = name;

this.age = age;

//retrun this; //默认有这一行。new一个构造函数,返回一个对象

}

var fn1 = new Foo(‘smyhvae’, 26);

var fn2 = new Foo(‘vae’,30); //new 多个实例对象

与普通函数相比,构造函数有以下明显特点:

  • 用new关键字调用。

  • 不需要用return显式返回值的,默认会返回this,也就是新的实例对象。

  • 建议函数名的首字母大写,与普通函数区分开。

参考链接:

当new之后,this会先变成一个空对象,然后通过this.name = name来赋值。

构造函数的扩展

上图中发现,数组、对象、函数也有构造函数,它们的构造函数是Array、Object、function。实际开发中,都推荐前面的书写方式。

原型规则

原型规则是学习原型链的基础。原型规则有五条,下面来讲解。

规则1

所有的引用类型(数组、对象、函数),都具有对象特性,都可以自由扩展属性。null除外。

举例:

规则2

所有的引用类型(数组、对象、函数),都有一个_proto_属性,属性值是一个普通的对象。_proto_的含义是隐式原型。

其实,规则2是规则1的特例,只不过,js语法帮我们自动加了 规则2。

规则三

所有的函数(不包括数组、对象),都有一个prototype属性,属性值是一个普通的对象。prototype的含义是显式原型。(实例没有这个属性)

规则四

所有的引用类型(数组、对象、函数),_proto_属性指向它的构造函数的prototype值。

总结:以上四条,要先理解清楚,然后再来看下面的第五条。

规则五

当试图获取一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的_proto_中寻找(即它的构造函数的prototype)。

举例代码1:

//创建方法

function Foo(name) {

this.name = name;

}

Foo.prototype.alertName = function () {// 既然 Foo.prototype
是普通的对象,那也允许给它添加额外的属性 alertName

console.log(this.name);

}

var fn = new Foo(‘smyhvae’);

fn.printName = function () {

console.log(this.name);

}

//测试

fn.printName(); //输出结果:smyhvae

fn.alertName(); //输出结果:smyhvae

上方代码中,虽然 alertName 不是 fn
自身的属性,但是会从它的构造函数的prototype里面找。

扩展:遍历循环对象自身的属性

我们知道,for
in循环可以遍历对象。针对上面的那个fn对象,它自身有两个属性:name、printName,另外从原型中找到了第三个属性alertName。现在,如果我们对fn进行遍历,能遍历到两个属性还是三个属性呢?

答案:两个。因为,高级浏览器中已经在 for
in循环中屏蔽了来自原型的属性。但是,为了保证代码的健壮性,我们最好自己加上判断
,手动将第三个属性屏蔽掉:

for (var item in fn) {

if (fn.hasOwnProperty(item)) {

console.log(item);

}

}

原型链

还是拿上面的举例代码1举例,如果此时在最后面加一行代码:

fn.toString(); //去 fn.proto.proto 中查找 toString()方法

上面的代码中,fn直接调用了
toString()方法,这是因为它通过原型链,去_proto_的_proto_里找到了Object,而Object是有toString()方法的。

instanceof

格式:

对象 instanceof 构造函数

instanceof的作用:用于判断引用类型属于哪个构造函数

例1:判断一个变量是否为数组: 变量 instanceof Array

例2:

function Person(){

}

//p—>Person.prototype—>Object.prototype—>null

var p = new Person();

//构造函数的原型是否在 p 对象的原型链上!

console.log(p instanceof Person);

例3:

fn instanceof Foo

上面这句话,判断逻辑是:fn 的_proto_一层一层往上找,看能否对应到
Foo.prototype

原型链如下:(重要)

注意,Object这个构造方法的显式原型是null,这是一个特例。

issues 101补充:通过原型链查找时,如果你找的是一个属性的话,则返回
undefined,如果你找的是一个方法,则报错。

常见题目

  • 如何准确判断一个变量是数组类型

  • 写一个原型链继承的例子

  • zepto(或其他框架)源码中如何使用原型链

下面分别讲解。

题目一:如何准确判断一个变量是数组类型

答案:

var arr1 = [];

console.log(arr1 instanceof Array); //打印结果:true。

console.log(typeof arr1); //打印结果:object。提示:typeof
方法无法判断是否为数组

上方代码表明,只能通过 instanceof 来判断是否为数组。而 typeof
的打印结果是 object。

题目二:写一个原型链继承的例子

  1. //封装DOM查询

  2. function Elem(id){

  3. this.elem = document.getElementById(id)

  4. }

  5. Elem.prototype.html = function (val){

  6. var elem = this.elem

  7. if (val) {

  8. elem.innerHTML = val

  9. return this;//链式操作

  10. } else {

  11. return elem.innerHTML

  12. }

  13. }

  14. Elem.prototype.on = function(type, fn){

  15. var elem = this.elem

  16. elem.addEventListener(type, fn)//用于向指定元素添加时间句柄

  17. return this;

  18. }

  19. var div1 = new Elem(‘div1’)//把对应id写在这里

  20. // console.log(div1.html())

  21. div1.html(‘

    hello

    World

    ‘).on(‘click’,function(){//链式操作

  22. alert(‘clicked’)

  23. }).html(‘

    javascript

    ‘)

参考链接:


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!