function myNew(){ //建立一個空對象 let obj = new Object(); //獲取構造函數 let Constructor = [].shift.call(arguments); //連接到原型 obj.__proto__ = Constructor.prototype; //綁定this值 let result = Constructor.apply(obj,arguments);//使用apply,將構造函數中的this指向新對象,這樣新對象就能夠訪問構造函數中的屬性和方法 //返回新對象 return typeof result === "object" ? result : obj;//若是返回值是一個對象就返回該對象,不然返回構造函數的一個實例對象 }
能夠歸納爲如下四步:
1.建立一個空對象
2.連接到原型
3.綁定this值
4.返回新對象面試
這裏提供兩種寫法segmentfault
Function.prototype.myCall = function(obj, ...arg){ //咱們要讓傳入的obj成爲, 函數調用時的this值. obj._fn_ = this; //在obj上添加_fn_屬性,值是this(要調用此方法的那個函數對象)。 obj._fn_(...arg); //在obj上調用函數,那函數的this值就是obj. delete obj._fn_; // 再刪除obj的_fn_屬性,去除影響. //_fn_ 只是個屬性名 你能夠隨意起名,可是要注意可能會覆蓋obj上原本就有的屬性 }
Function.prototype.myCall = function(obj){ if(obj === null || obj === undefined){ obj = window; } else { obj = Object(obj); } let arg = []; for(let i = 1 ; i<arguments.length ; i++){ arg.push( 'arguments[' + i + ']' ) ; // 這裏要push 這行字符串 而不是直接push 值 // 由於直接push值會致使一些問題 // 例如: push一個數組 [1,2,3] // 在下面👇 eval調用時,進行字符串拼接,JS爲了將數組轉換爲字符串 , // 會去調用數組的toString()方法,變爲 '1,2,3' 就不是一個數組了,至關因而3個參數. // 而push這行字符串,eval方法,運行代碼會自動去arguments裏獲取值 } obj._fn_ = this; eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接,JS會調用arg數組的toString()方法,這樣就傳入了全部參數 delete obj._fn_; }
apply的寫法跟call接近數組
Function.prototype.myApply = function(obj,arr){ if(obj === null || obj === undefined){ obj = window; } else { obj = Object(obj); } let args = []; let val ; for(let i = 0 ; i<arr.length ; i++){ args.push( 'arr[' + i + ']' ) ; } obj._fn_ = this; val = eval( 'obj._fn_(' + args + ')' ) delete obj._fn_; return val }
一樣提供兩種寫法app
Function.prototype.myBind = function(obj,...arg1){ return (...arg2) => { let args = arg1.concat(arg2); let val ; obj._fn_ = this; val = obj._fn_( ...args ); delete obj._fn_; return val } }
Function.prototype.myBind = function(obj){ let _this = this; let argArr = []; let arg1 = []; for(let i = 1 ; i<arguments.length ; i++){ // 從1開始 arg1.push( arguments[i] ); // 這裏用arg1數組收集下參數 // 獲取arguments是從1開始, 但arg1要從 0(i-1)開始 // 如果用Array.prototype.slice.call(argument)就方便多了 argArr.push( 'arg1[' + (i - 1) + ']' ) ; // 若是用arguments在返回的函數裏運行 會獲取不到這個函數裏的參數了 } return function(){ let val ; for(let i = 0 ; i<arguments.length ; i++){ // 從0開始 argArr.push( 'arguments[' + i + ']' ) ; } obj._fn_ = _this; val = eval( 'obj._fn_(' + argArr + ')' ) ; delete obj._fn_; return val }; }
淺克隆比較簡單,直接遍歷老對象逐個賦值便可。函數
function clone(target) { let cloneTarget = {}; for (const key in target) { cloneTarget[key] = target[key]; } return cloneTarget; };
深克隆須要考慮數據是基本類型仍是引用類型
另外,引用類型中數組和對象要單獨處理,所以須要二次判斷
最後還要考慮對象的嵌套引用,防止堆棧溢出post
function clone(target, map = new Map()) { // 引用類型 if (typeof target === 'object') { // 判斷是否爲數組 let cloneTarget = Array.isArray(target) ? [] : {}; // 用map來判斷該對象是否已經克隆過,方式嵌套引用 if (map.get(target)) { return map.get(target); } map.set(target, cloneTarget); for (const key in target) { cloneTarget[key] = clone(target[key], map); } return cloneTarget; } // 基本類型 else { return target; } };
本文參考:
https://www.jianshu.com/p/9ce...
https://www.jianshu.com/p/3b6...
http://www.javashuo.com/article/p-dwtfvyno-k.htmlthis