1.题目如何准确判断一个变量是数组写一个原型链继承的例子继承实现的其他方式es6 实现继承的底层原理是什么描述new一个对象的过程zepto及其他源码中如何使用原型链2.知识点2.1 构造函数特点:以大写字母开头function Foo(name,age){ //var obj = {} //this = {} this.name = name; this.age = age; this.class = ‘class1’ // return this}var f1 = new Foo(’liming’,19);扩展var o = {} 是 var o = new Object() 的语法糖var a = [] 是 var a = new Array() 的语法糖function Foo(){} 相当于 var Foo = new Function(){}2.2 原型规则五条规则:1.所有引用类型(对象,数组,函数)都具有对象特性,即可以自由扩展属性2.所有引用类型(对象,数组,函数)都具有一个__proto__(隐式原型)属性,是一个普通对象3.所有的函数都具有prototype(显式原型)属性,也是一个普通对象4.所有引用类型(对象,数组,函数)proto__值指向它构造函数的prototype5.当试图得到一个对象的属性时,如果变量本身没有这个属性,则会去他的__proto__中去找for (var key in object) { //高级浏览器中已经屏蔽了来自原型的属性 //建议加上判断保证程序的健壮性 if (object.hasOwnProperty(key)) { console.log(object[key]); }}2.3 原型链obj. proto . proto . proto __ … Object.prototype === nullinstanceof 用于判断引用类型属于哪个构造函数obj instanceob Foo实际意义:判断 Foo.prototype 在不在 obj的原型链上3.题目解答3.1 如何准确判断一个变量是数组arr instanceof Array3.2 写一个原型链继承的例子封装dom查询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,fun){ var elem = this.elem; elem.addEventListener(type,fun); return this;}var div1 = new Elem(‘id1’);div1.html(“test”).on(‘click’,function(){ console.log(‘点击’);})3.3 继承实现的其他方式3.3.1 原型继承 var obj = { 0:‘a’, 1:‘b’, arr:[1] } function Foo(arr2){ this.arr2 = [1] } Foo.prototype = obj; var foo1 = new Foo(); var foo2 = new Foo(); foo1.arr.push(2); foo1.arr2.push(2); console.log(foo2.arr); //[1,2] console.log(foo2.arr2); //[1]优点:实现简单缺点:1.无法向父类构造函数传参2.同时new两个对象时改变一个对象的原型中的引用类型的属性时,另一个对象的该属性也会修改。因为来自原型对象的引用属性是所有实例共享的。3.3.2 构造继承 function Super(b){ this.b = b; this.fun = function(){} } function Foo(a,b){ this.a = a; Super.call(this,b); } var foo1 = new Foo(1,2); console.log(foo1.b);优点:可以向父类传参,子类不会共享父类的引用属性缺点:无法实现函数复用,每个子类都有新的fun,太多了就会影响性能,不能继承父类的原型对象。3.3.3 组合继承function Super(){ // 只在此处声明基本属性和引用属性 this.val = 1; this.arr = [1];}// 在此处声明函数Super.prototype.fun1 = function(){};Super.prototype.fun2 = function(){};//Super.prototype.fun3…function Sub(){ Super.call(this); // 核心 // …}Sub.prototype = new Super(); 优点:不存在引用属性共享问题,可传参,函数可复用缺点:父类的属性会被实例化两次,获取不到真正实例父类(无法区分实例是父类创建还是父类创建的)优化: function Super(b){ this.b = b; this.fun = function(){} } Super.prototype.c = function(){console.log(1111)} function Foo(a,b){ this.a = a; Super.call(this,b); } Foo.prototype = Super.prototype; //修复构造函数: var foo1 = new Foo(1,2);缺点:无法区分实例是父类创建还是子类创建的3.3.4 寄生组合继承 function Super(b){ this.b = b; } Super.prototype.c = function(){console.log(1111)} function Foo(a,b){ this.a = a; Super.call(this,b); } var f = new Function(); f.prototype = Super.prototype; Foo.prototype = new f(); //等同于 Foo.prototype = Object.create(Super.prototype); var foo1 = new Foo(1,2);对父类的prototype进行一次寄生,即包装成一个空对象的prototype,再把这个对象实例化出来作为子类的peototype。缺点:无法区分实例是父类创建还是子类创建的可以添加以下代码:Foo.prototype.constructor = Foo这种解决方法不能用于上面的组合优化方法,因为子类父类引用的是同一个原型对象,修改会同时修改。总结:继承主要是实现子类对父类方法,属性的复用。来自原型对象的引用属性是所有实例共享的,所以我们要避免从原型中继承属性。在构造函数中通过call函数可以继承父类构造函数的属性和方法,但是通过这种方式实例化出来的实例会将父类方法多次存储,影响性能。通过组合继承我们使用call继承属性,使用原型继承方法,可以解决以上两个问题,但是通过这种方式实例化出来的对象会存储两份父类构造函数中的属性。用父类的原型构造一个新对象作为子类的原型,就解决了多次存储的问题,所以最终的寄生组合继承就是最佳继承方式,它的缺点就是书写起来比较麻烦。3.3.6 node源码中的继承实现function inherits(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } });}; function Stream(){ //…}function OutgoingMessage() { Stream.call(this); //…}inherits(OutgoingMessage, Stream);OutgoingMessage.prototype.setTimeout = …以上是寄生组合继承的一个实例。1.在OutgoingMessage构造函数中通过call继承Stream构造中的属性。2.调用inherits方法继承Stream原型中的属性。3.扩展OutgoingMessage自身原型的函数。inherits方法中使用了Object.create方法,该方法的作用是通过指定的原型对象和属性创建一个新的对象。ctor.prototype=Object.create(superCtor.prototype,{…..});该方法实际上就做了我们上面寄生组合继承中的工作var f = new Function();f.prototype =superCtor.prototype;return new f();后面的参数是给原型对象添加属性,可选属性(非必填),即把自身作为新创建对象的构造函数。value: 表示constructor 的属性值;writable: 表示constructor 的属性值是否可写;[默认为: false]enumerable: 表示属性constructor 是否可以被枚举;[默认为: false]configurable: 表示属性constructor 是否可以被配置,例如 对obj.a做 delete操作是否允许;[默认为: false]3.4 es6继承的实现方式参考我这篇文章:https://segmentfault.com/a/11…3.5 描述new一个对象的过程创建一个对象{}.proto = 构造函数.prototypethis指向这个对象执行代码即对this赋值返回this3.6 zepto及其他源码中如何使用原型链var Zepto = (function(){ var $,zepto = {} // …省略N行代码… $ = function(selector, context){ return zepto.init(selector, context) } zepto.init = function(selector, context) { var dom // 针对参数情况,分别对dom赋值 // 最终调用 zepto.Z 返回的数据 return zepto.Z(dom, selector) } fnction Z(dom, selector) { var i, len = dom ? dom.length : 0 for (i = 0; i < len; i++) this[i] = dom[i] this.length = len this.selector = selector || ’’ } zepto.Z = function(dom, selector) { return new Z(dom, selector) } $.fn = { // 里面有若干个工具函数 } zepto.Z.prototype = Z.prototype = $.fn // …省略N行代码… return $})()window.Zepto = Zeptowindow.$ === undefined && (window.$ = Zepto)