黑马程序员技术交流社区

标题: 深入理解JavaScript系列(46):代码复用模式(推荐篇) [打印本页]

作者: suifeng199106    时间: 2019-12-30 18:58
标题: 深入理解JavaScript系列(46):代码复用模式(推荐篇)
本文介绍的四种代码复用模式都是最佳实践,推荐大家在编程的过程中使用。
模式1:原型继承
原型继承是让父对象作为子对象的原型,从而达到继承的目的:深圳记账代理 http://www.caishui114.com/
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
复制代码

而且,也可以更细粒度地在第二个参数上定义属性:
// 首先,定义一个新对象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,也就是没有toStringvalueOf方法,所以alert(man);的时候会出错,但alert(man.car);是没问题的。注册公司需要多少钱
模式2:复制所有属性进行继承
这种方式的继承就是将父对象里所有的属性都复制到子对象上,一般子对象可以使用父对象的数据。
先来看一个浅拷贝的例子:深圳会计公司 http://www.0755dyx.com/
/* 浅拷贝 */function extend(parent, child) {    var i;    child = child || {};    for (i in parent) {        if (parent.hasOwnProperty(i)) {            child = parent;        }    }    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 === 'object') {                child = (toStr.call(parent) === astr) ? [] : {};                extendDeep(parent, child);            } else {                child = parent;            }        }    }    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] = givingObj.prototype[arguments];        }    }    // 如果不指定第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"
复制代码

总结
就不用总结了吧。


作者: 柠檬leung不酸    时间: 2020-5-18 14:28
你说 “生命在于不断超越自己”,那来到这里的我已经赢了一半,你还会等我么?




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2