JS 拷貝是一個(gè)在我們開(kāi)發(fā)中經(jīng)常遇到的一個(gè)問(wèn)題,我們通過(guò)使用extend這個(gè)函數(shù)來(lái)實(shí)現(xiàn)一些JS的復(fù)用。
比如下面的代碼:
var _default = { address: "上海市靜安區(qū)", sex: "男" };
var user = $.extend({}, _default,{name:"小亮", age:12});
console.log(user) //=> {name:"小亮", address: "上海市靜安區(qū)",age:12}
這是一個(gè)開(kāi)發(fā)中經(jīng)常遇到的一個(gè)場(chǎng)景,我們通過(guò)復(fù)用一些默認(rèn)的配置,減少了代碼開(kāi)發(fā)量。這里我是用的$.extend是jQuery提供的一個(gè)靜態(tài)拷貝函數(shù)。它在這里的作用就是把_default 和 {name:"小亮",age:12}的屬性合并到 {} 中,并且返回。
自己如何實(shí)現(xiàn)
作為一個(gè)程序員,我們不僅僅需要知其然,還是知其所以然,所以我們不僅是需要知道怎么使用,也需要明白其中的實(shí)現(xiàn)和原理,以及一些我們需要注意的事項(xiàng)。
第一步: 入口參數(shù)處理
extend = function (x, y, z, ......) { ...... }
extend這個(gè)函數(shù)的功能是把除第一個(gè)參數(shù)外,其它參數(shù)的屬性合并到第一個(gè)參數(shù),那我們是不知道總共有多少個(gè)參數(shù),所以我們需要使用到 arguments 這個(gè)對(duì)象去獲取參數(shù),當(dāng)然了,如果是在ES6的情況下,我們就不需要使用arguments這個(gè)參數(shù)對(duì)象了。
extend = function (x, y, z, ......) { console.log(arguments) //=> [x,y,z ......] console.log(arguments[0]) //=> x console.log(arguments[1]) //=> y ...... }
這里arguments 是一個(gè)類(lèi)數(shù)組對(duì)象,可以通過(guò)arguments的下標(biāo)獲取相關(guān)的參數(shù)值,但是沒(méi)有實(shí)現(xiàn)數(shù)組相關(guān)的方法。所以,我們有時(shí)候需要把a(bǔ)rguments這個(gè)參數(shù)對(duì)象轉(zhuǎn)化為數(shù)組。
var argus = Array.prototype.slice.call(arguments);
這樣我們就能夠獲取extend函數(shù)的不定參數(shù)
第二步: 數(shù)據(jù)拷貝
在開(kāi)始進(jìn)行數(shù)據(jù)拷貝之前,我們應(yīng)該需要確認(rèn)兩個(gè)問(wèn)題。
我們獲取到參數(shù)以后,第一個(gè)參數(shù),應(yīng)該是作為我們的base對(duì)象,后面的對(duì)象把他們的屬性放到這個(gè)base對(duì)象上,而且是依次執(zhí)行的。 這樣的話(huà),后面屬性值會(huì)把前面的屬性值給進(jìn)行覆蓋。
就是需要對(duì)參數(shù)類(lèi)型進(jìn)行判斷, 按照日常的開(kāi)發(fā)需求來(lái)說(shuō),我們這里是只需要純 Object 對(duì)象,那么像Number,Boolean,String,F(xiàn)unction, Array...... 這種如果出現(xiàn)在參數(shù)里面。那么從功能角度來(lái)說(shuō),這些數(shù)據(jù)是無(wú)用數(shù)據(jù),所以這些數(shù)據(jù)會(huì)被過(guò)濾掉。
Object.prototype.toString.call(obj) === "[object Object]" //=> true
那么我們開(kāi)始進(jìn)入正題,實(shí)現(xiàn)extend的拷貝
var isObj = function (obj) { return Object.prototype.toString.call(obj) === "[object Object]"; };
var extend = function(base) { // argus 表示除 base 后面的列表集合 var argus = Array.prototype.slice.call(arguments,1),obj,key; base = isObj(base) ? base : {}; for (var index = 0, length = argus.length; index < length; index++) { obj = argus[index]; if (isObj(obj)) { // 防止非純Object對(duì)象 for (key in obj) { if (obj.hasOwnProperty(key)) { base[key] = obj[key]; } } } } return base; }
測(cè)試用例
extend({},{a:1},{b:1}); //=> Object {a: 1, b: 1}
extend({a:1},{b:1}); //=> Object {a:1, b:1}
extend({},[1,2,3]); //=> Object {}
extend({},{aa : function() { alert("aa")}}); //=>Object{aa:()}
extend(null,{aa : 111 }); //=>Object{aa :111}
到此,我們已經(jīng)完成了 extend 這個(gè)函數(shù)基本骨架。