关键词:
JS 的对象机制并不会自动执行复制行为,由于其他语言中表现出来的复制行为,因此 JS 开发者也想出了一个方式来模拟类的复制行为,这个方法就是混入。我们先看第一种,显式混入。
// 非常简单的 mixin() 例子 function mixin(sourceObj, targetObj) for (var key in sourceObj) // 只会在不存在的情况下复制 if (!(key in targetObj)) targetObj[key] = sourceObj[key] return targetObj; var Vehicle = engines: 1, ignition: function() console.log(\'Turning on my engine.\'); , drive: function() this.ignition(); console.log(\'Steering and moving forward!\'); var Car = mixin(Vehicle, wheel: 4, drive: function() Vehicle.drive.call(this); console.log(\'Rolling on all \' + this.wheel + \' wheels!\'); )
我们处理的已经不再是类了,因为在 JS 中不存在类,Vehicle 和 Car 都是对象。现在 Car 中就有了一份 Vehicle 属性和函数的副本。从技术角度来说,函数实际上没有被复制,复制的是函数引用。Car 已经有了 drive 属性,所以这个属性引用没有被 mixin 重写,从而保留了 Car 中定义的同名属性,实现了 ‘子类’ 对 ‘父类’ 属性的重写。
// 另一种混入函数,可能又重写风险 function mixin(sourceObj, targetObj) for (var key in sourceObj) targetObj[key] = sourceObj[key]; return targetObj;
var Vehicle = // ...
// 首先创建一个空对象并把 Vehicle 对内容复制进去 var Car = mixin( Vehicle, ); // 然后把新内容复制到 Car 中 mixin( wheel: 4, drive: function() // ... , Car)
这两种方法都可以把不重叠对内容从 Vehicle 中显示复制到 Car 中。‘混入’ 这个名字来源与这个过程对另一种解释: Car 中混合了 Vehicle 的内容,所以这叫混合复制。复制操作完成后, Car 和 Vehicle 分离了,向 Car 中添加属性不会影响到 Vehicle,反之亦然。
// 传统的 JS 类,Vehicle function Vehicle() this.engines = 1; Vehicle.prototype.ignition = function () console.log(\'Turning on my engine.\'); Vehicle.prototype.drive = function () this.ignition(); console.log(\'Steering and moving forward!\'); // 寄生类 Car function Car() // 首先, Car 是一个 Vehicle var car = new Vehicle(); // 接着我们对 Car 进行定制 car.wheels = 4; // 保存到 Vehicle::drive() 的特殊引用 var vehDrive = car.drive; // 重写 Vehicle::drive() car.drive = function() vehDrive.call(this); console.log(\'Rolling on all \' + this.wheels + \' wheels!\'); return car; var myCar = new Car() myCar.drive();
首先我们复制一份 Vehicle 父类的定义,然后混入子类的定义(如果需要的话保留父类的特殊引用),然后用这个复合对象构建实例。
你不知道的js系列(12)-声明提升(代码片段)
我们直觉上会认为JavaScript代码在执行时是由上到下一行一行执行的。但实际这并不完全正确 a=2;vara;console.log(a);这里可能会认为是undefined,因为vara声明在a=2之后。实际输出了2。 console.log(a);vara=2;鉴于上面的代码可能会是2... 查看详情
你不知道的js系列(14)-闭包无处不在(代码片段)
上一节的闭包是为了解释如何使用闭包而人为地在结构上进行修饰,在昨天的闭包基础上,我们可以更加灵活的使用闭包functionwait(message) setTimeout(functiontimer() console.log(message) ,1000)wait(‘hello,consure‘);内部函数timer... 查看详情
你不知道的js系列(30)-对象属性(可计算属性名)(代码片段)
如果你需要通过表达式来计算属性名,[]操作符就派上用场了。ES6中使用[]包裹一个表达式来当作属性名varprefix=‘foo‘;varmyObject= [prefix+‘bar‘]:‘hello‘, [prefix+‘baz‘]:‘world‘,myObject[‘foobar‘];//hellomyObject[‘foobaz‘];//world... 查看详情
你不知道的js系列(33)-对象复制(代码片段)
JS初学者最常见的问题之一就是如何复制一个对象。看起来应该有一个内置的copy()方法,实际上比想象中的更复杂,我们无法选择一个默认的复制算法functionanotherFunction()/**...*/;varanotherObject=c:true;varanotherArray=[];varmyObject= a:2, ... 查看详情
你不知道的js系列(39)-对象遍历(代码片段)
for循环可以遍历数组varmyArray=[1,2,3];for(vari=0;i<myArray.length;i++) console.log(myArray[i])//123ES5增加了数组的辅助迭代器,包括forEach(...)、every(...)、some(...)forEach(...)会遍历数组中的所有值并忽略回调函数的返回值every(...)会一直运行直... 查看详情
你不知道的js系列(13)-什么是闭包(代码片段)
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行functionfoo() vara=2; functionbar() console.log(a); returnbarvarbaz=foo();baz();//2——朋友,这就是闭包的效果在foo... 查看详情
你不知道的js系列(29)-对象属性(代码片段)
存储在对象容器内部的是这些属性的名称,它们就像指针(从技术角度来说是引用)一样,指向这些值真正的存储位置。 varmyObject= a:2myObject.a;//2myObject[‘a‘];//2.语法通常被称为‘属性访问’,[]语法通常被称为&lsqu... 查看详情
你不知道的js系列(22)-thisnew绑定(代码片段)
在传统的面向类的语言中,“构造函数“是类中的一些特殊方法,使用new初始化类时会调用类中的构造函数。通常的形式是这样的something=newMyClass(..);然而JavaScript中new的机制实际上和面向类的语言不同。它们只是被new操作... 查看详情
你不知道的js系列(7)-欺骗词法作用域(代码片段)
如果词法作用域完全由写代码期间函数所声明的位置来定义,怎样才能在运行时来“修改”词法作用域呢?有些人喜欢特殊的办法来解决遇到的问题。我们规定词法作用域是代码写在哪里决定的,一旦决定了无法更改,因... 查看详情
你不知道的js系列-引擎怎么查找变量(代码片段)
对代码进行处理的三个角色引擎:从头到尾负责整个JavaScript程序的编译和执行过程编译器:负责语法分析及代码生成等作用域:负责收集并维护所有变量的查询 vara=2;编译器首先会将这段程序分解成词法单元,然后将词法单... 查看详情
你不知道的js系列(19)-this调用位置(代码片段)
我们排除了一些对于this对错误理解并且明白了每个函数的this是在调用时被绑定的,完全取决于函数的调用位置。寻找调用位置就是寻找“函数被调用的位置”,但是做起来并没有这么简单,因为某些编程模式可能会隐藏... 查看详情
你不知道的js系列(5)-词法作用域(代码片段)
作用域分为两种,一种是词法作用域,一种是动态作用域,我们先看第一种,词法作用域 词法作用域就是定义在词法阶段的作用域(编译器的第一个工作阶段叫做词法化,词法化的过程会对源代码中的字符进行检查)。换句... 查看详情
你不知道的js系列(23)-this绑定优先级(代码片段)
我们首先来看下隐式绑定和显示绑定哪个优先级更高functionfoo() console.log(this.a)varobj1= a:2, foo:foovarobj2= a:3, foo:fooobj1.foo();//2obj2.foo();//3obj1.foo.call(obj2);//3obj2.foo.call(obj1);//2这个例子可以看到,显示绑定优先级比 查看详情
你不知道的sring(系列三)(代码片段)
AbstractStringBuilderStringBuffer和StringBuilder都继承了AbstractStringBuilder,很多方法都是直接super的父类AbstractStringBuilder的方法,所以我们分析下AbstractStringBuilder的源码.1.成员变量AbstractStringBuilder和String一样,在其内部都是以字符数组的形... 查看详情
《你不知道的js(中卷)》混合对象“类”(代码片段)
四、混合对象“类”:? 在研究类的具体机制之前,首先介绍面向类的设计模式:实例化(instantiation)、继承(inheritance)和(相对)多态(polymorphism)。一)、类理论:? 面向对象编程强调的是数据和操作数据的行为本质上是... 查看详情
《你不知道的js(中卷)》关于this(代码片段)
一、关于this:一)、为什么要用this?functionidentity() returnthis.name.toUpperCase();varme= name:"Kyle";varyou= name:"Reader";identity.call(me);//Kyleidentity.call(you);//Reader? 观察上面的代码,使 查看详情
你不知道的javascript系列中(12)-特殊数值无穷数(代码片段)
熟悉传统语言(C)的开发人员可能都遇到过编译错误(compilererror)或者运行时错误(runtimeexception),例如“除以0”。然后在JavaScript中上例的结果为Infinity vara=1/0;//Infinityvara=-1/0;//-Infinity如果除法运算中的一个操作数... 查看详情
你不知道的javascript系列中(12)-特殊数值无穷数(代码片段)
熟悉传统语言(C)的开发人员可能都遇到过编译错误(compilererror)或者运行时错误(runtimeexception),例如“除以0”。然后在JavaScript中上例的结果为Infinity vara=1/0;//Infinityvara=-1/0;//-Infinity如果除法运算中的一个操作数... 查看详情