原型和原型链
题目(答案见每题下方空白处)
1.如何准确判断一个变量是数组类型
用instanceof Array,返回为true就说明是
2.写一个原型链继承的例子
function Animal(){ this.eat = function(){ console.log('animal eat'); } } function Cat(){ this.say = function(){ console.log('cat miao'); } } Cat.prototype = new Animal(); var mimi = new Cat(); mimi.eat();
function Elem(id){ this.elem = document.getElementById(id); } Elem.prototype.html = function(val){ var elem = this.elem; if(val){ elem.innerHTML = val; return this;//为了链式操作 } else{ return elem.innerHTML; } } Elem.prototype.on = function(type,fn){ var elem = this.elem; elem.addEventListener(type,fn); return this;//为了链式调用 } var div1 = new Elem('detail-page'); div1.html('<p>hello imooc</p>').on('click',function(){ alert('clicked'); }).html('<p>javascript</p>');
3.描述new一个对象的过程
分三步,假定该对象名为xm,则先是xm={},即xm为一个空对象,然后xm.__proto__ = object.prototype,最后调用call方法,xm.call()
或者另一种理解分四步,先是创建一个新对象,然后this指向这个新对象,之后执行代码,对this赋值,最后返回this
4.zepto(或其他框架)源码中如何使用原型链
(未完待续)
知识点
构造函数
先举一个构造函数的例子
构造函数一般函数名开头大写(约定俗成),函数内部用this指向来给对象赋值
整个过程如下:
1.this指向一个空对象
2.通过构造函数里的赋值,this得到众多属性值,赋值完成后,默认返回this
3.把这个对象赋值给f(也就是f.call())
构造函数扩展
var a = {}为var a = new Object()的语法糖(即功能上没有改变,只是改变了语法,使得代码更易阅读)
var a = []为var a = new Array()的语法糖
function Foo(){}为var Foo = new Function()的语法糖
原型规则
1.所有的引用类型都有对象特性,即可以自由扩展属性(null除外)
2.所有引用类型都有一个__proto__属性(隐式原型),属性值为一个普通对象
(这里一个问题,object的__proto用instanceof判断,返回的是false)
3.所有的函数都有一个prototype属性(显式原型),属性值为一个普通对象
4.所有引用类型的__proto__属性值指向其构造函数的prototype属性值
5.当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,就会去其__proto__(即其构造函数的prototype)中寻找(原型链的原理)
这里alertName这个属性(方法)在f实例中没有,所以就去其原型上查找,也就是Foo.prototype上查找,最后调用
其他注意点
如果遍历对象上的属性,for-in会遍历其对象本身以及原型上的所有属性(输出的是属性名)
而for-of只会遍历其对象本身的属性值(但必须是可遍历的,即iterable)
如果需要限制遍历对象本身的属性,则用Object对象的hasOwnProperty方法(for-of方法在新浏览器中基本解决了这一问题,但为了以防万一,还是加上为好)
原型链
每个函数都有一个prototype属性,而所有引用类型都有一个__proto__属性,指向其构造函数的prototype属性
这样一来就形成了一条以对象的__proto__属性(隐式属性)和函数的prototype属性(显式属性)构成的一条无形的链子,即原型链
但需要注意的是,Object这个构造函数的显式属性的隐式属性为null(即Object.prototype.__proto__为null)
这是JS的一个机制,为了避免原型链的死循环
instanceof
该方法用来判断引用类型的变量属于哪个构造函数
实际上的判断逻辑为原型链,也就是该实例的__proto__属性层层往上,能否对应上构造函数的prototype属性
也就可以回答了为什么Object实例的__proto__属性不是Object的实例(obj.__proto__.__proto__实际上为Object.prototype.__proto__,指向null,自然不等于Object.prototype)