JavaScript 代码复用推荐篇

2023-02-05 0 750

责任编辑如是说的三种标识符F83E43Se商业模式都是最差课堂教学,所推荐我们在程式设计的操作过程中采用。

商业模式1:蓝本承继

蓝本承继是让父第一类做为子第一类的蓝本,进而达至承继的目地:

function object(o) { function F() { } F.prototype = o; return new F();}// 要承继的父第一类var parent = { name: “Papa”};// 新第一类var child = object(parent);// 试验console.log(child.name); // “Papa”// 父缺省function Person() { // an “own” property this.name = “Adam”;}// 给蓝本加进新特性Person.prototype.getName = function () { return this.name;};// 建立新personvar papa = new Person();// 承继var kid = object(papa);console.log(kid.getName()); // “Adam”// 父构造函数function Person() { // an “own” property this.name = “Adam”;}// 给蓝本加进新特性Person.prototype.getName = function () { return this.name;};// 承继var kid = object(Person.prototype);console.log(typeof kid.getName); // “function”,即使是在蓝本里表述的console.log(typeof kid.name); // “undefined”, 即使只承继了蓝本

与此同时,ECMAScript5也提供了类似于的两个方式叫作Object.create用作承继第一类,用语如下表所示:

/* 采用旧版的ECMAScript 5提供更多的机能 */var child = Object.create(parent);var child = Object.create(parent, { age: { value: 2} // ECMA5 descriptor});console.log(child.hasOwnProperty(“age”)); // true

所以,也能更mammalian地在第三个模块上表述特性:

// 具体来说,表述两个新第一类manvar man = Object.create(null);// 接著,建立包含特性的配置设置// 特性设置为可写,可枚举,可配置var config = { writable: true, enumerable: true, configurable: true};// 通常采用Object.defineProperty()来加进新特性(ECMAScript5支持)// 现在,为了方便,我们自表述两个封装函数var defineProp = function (obj, key, value) { config.value = value; Object.defineProperty(obj, key, config);}defineProp(man, car, Delorean);defineProp(man, dob, 1981);defineProp(man, beard, false);

所以,承继就这么能做了:

var driver = Object.create( man );defineProp (driver, topSpeed, 100mph);driver.topSpeed // 100mph

但是有个地方需要注意,就是Object.create(null)建立的第一类的蓝本为undefined,也就是没有toString和valueOf方式,所以alert(man);的时候会出错,但alert(man.car);是没问题的。

商业模式2:复制所有特性进行承继

这种方式的承继就是将父第一类里所有的特性都复制到子第一类上,一般子第一类能采用父第一类的数据。先来看两个浅拷贝的例子:

/* 浅拷贝 */function extend(parent, child) { var i; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { child[i] = parent[i]; } } return child;}var dad = { name: “Adam” };var kid = extend(dad);console.log(kid.name); // “Adam”var dad = { counts: [1, 2, 3], reads: { paper: true }};var kid = extend(dad);kid.counts.push(4);console.log(dad.counts.toString()); // “1,2,3,4”console.log(dad.reads === kid.reads); // true

标识符的最后一行,你能发现dad和kid的reads是一样的,也就是他们采用的是同两个引用,这也就是浅拷贝带来的问题。我们再来看一下深拷贝:

/* 深拷贝 */function extendDeep(parent, child) { var i, toStr = Object.prototype.toString, astr = “[object Array]”; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { if (typeof parent[i] === object) { child[i] = (toStr.call(parent[i]) === astr) ? [] : {}; extendDeep(parent[i], child[i]); } else { child[i] = parent[i]; } } } return child;}var dad = { counts: [1, 2, 3], reads: { paper: true }};var kid = extendDeep(dad);kid.counts.push(4);console.log(kid.counts.toString()); // “1,2,3,4”console.log(dad.counts.toString()); // “1,2,3”console.log(dad.reads === kid.reads); // falsekid.reads.paper = false;

深拷贝以后,两个值就不相等了,bingo!

商业模式3:混合(mix-in)

混入就是将两个第一类的两个或多个(或全部)特性(或方式)复制到另外两个第一类,我们举两个例子:

function mix() { var arg, prop, child = {}; for (arg = 0; arg < arguments.length; arg += 1) { for (prop in arguments[arg]) { if (arguments[arg].hasOwnProperty(prop)) { child[prop] = arguments[arg][prop]; } } } return child;}var cake = mix( { eggs: 2, large: true }, { butter: 1, salted: true }, { flour: 3 cups }, { sugar: sure! } );console.dir(cake);

mix函数将所传入的所有模块的子特性都复制到child第一类里,以便产生两个新第一类。那如何我们只想混入部分特性呢?该个如何做?其实我们能采用多余的模块来表述需要混入的特性,例如mix(child,parent,method1,method2)这样就能只将parent里的method1和method2混入到child里。上标识符:

// Car var Car = function (settings) { this.model = settings.model || no model provided; this.colour = settings.colour || no colour provided;};// Mixinvar Mixin = function () { };Mixin.prototype = { driveForward: function () { console.log(drive forward); }, driveBackward: function () { console.log(drive backward); }};// 表述的2个模块分别是被混入的第一类(reciving)和从哪里混入的第一类(giving)function augment(receivingObj, givingObj) { // 如果提供更多了指定的方式名称的话,也就是模块多余3个 if (arguments[2]) { for (var i = 2, len = arguments.length; i < len; i++) { receivingObj.prototype[arguments[i]] = givingObj.prototype[arguments[i]]; } } // 如果不指定第3个模块,或者更多模块,就混入所有的方式 else { for (var methodName in givingObj.prototype) { // 检查receiving第一类内部不包含要混入的名字,如何包含就不混入了 if (!receivingObj.prototype[methodName]) { receivingObj.prototype[methodName] = givingObj.prototype[methodName]; } } }}// 给Car混入特性,但是值混入driveForward 和 driveBackward*/augment(Car, Mixin, driveForward, driveBackward);// 建立新第一类Carvar vehicle = new Car({ model: Ford Escort, colour: blue });// 试验是否成功得到混入的方式vehicle.driveForward();vehicle.driveBackward();

该方式采用起来就比较灵活了。

商业模式4:借用方式

两个第一类借用另外两个第一类的两个或两个方式,而这两个第一类之间不会有什么直接联系。不用多解释,直接用标识符解释吧:

var one = { name: object, say: function (greet) { return greet + , + this.name; }};// 试验console.log(one.say(hi)); // “hi, object”var two = { name: another object};console.log(one.say.apply(two, [hello])); // “hello, another object”// 将say赋值给两个变量,this将指向到全局变量var say = one.say;console.log(say(hoho)); // “hoho, undefined”// 传入两个回调函数callbackvar yetanother = { name: Yet another object, method: function (callback) { return callback(Hola); }};console.log(yetanother.method(one.say)); // “Holla, undefined”function bind(o, m) { return function () { return m.apply(o, [].slice.call(arguments)); };}var twosay = bind(two, one.say);console.log(twosay(yo)); // “yo, another object”// ECMAScript 5给Function.prototype加进了两个bind()方式,以便很容易采用apply()和call()。if (typeof Function.prototype.bind === undefined) { Function.prototype.bind = function (thisArg) { var fn = this,slice = Array.prototype.slice,args = slice.call(arguments, 1); return function () { return fn.apply(thisArg, args.concat(slice.call(arguments))); }; };}var twosay2 = one.say.bind(two);console.log(twosay2(Bonjour)); // “Bonjour, another object”var twosay3 = one.say.bind(two, Enchanté);console.log(twosay3()); // “Enchanté, another object”
举报/反馈

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务

BP宝库站

Hi,欢迎来到BP宝库,需要外包可联系qq:2405474279 WordPress、网站、SEO优化、小程序、爬虫、搭建外包服务应有尽有

我知道了