一、new運算符
語法
new constructor[([arguments])]
constructor
一個指定對象實例的類型的類或函數(shù)第煮。
arguments
一個用于被 constructor 調(diào)用的參數(shù)列表。
描述
當(dāng)代碼 new Foo(...)
執(zhí)行時,會發(fā)生以下事情:
- 一個繼承自
Foo.prototype
的新對象被創(chuàng)建砂竖。 - 使用指定的參數(shù)調(diào)用構(gòu)造函數(shù)
Foo
,并將this
綁定到新創(chuàng)建的對象秦叛。new Foo
等同于new Foo()
晦溪,也就是沒有指定參數(shù)列表,Foo
*不帶任何參數(shù)調(diào)用的情況挣跋。 - 由構(gòu)造函數(shù)返回的對象就是
new
表達(dá)式的結(jié)果三圆。如果構(gòu)造函數(shù)沒有顯式返回一個對象,則使用步驟1創(chuàng)建的對象避咆。(一般情況下舟肉,構(gòu)造函數(shù)不返回值,但是用戶可以選擇主動返回對象查库,來覆蓋正常的對象創(chuàng)建步驟)路媚。
根據(jù)上面的條件,我們就可以寫代碼了
function myNew(constructor, ...args) {
//以構(gòu)造器原型創(chuàng)建對象
let instance = Object.create(constructor.prototype);
//執(zhí)行構(gòu)造器樊销,并把構(gòu)造器內(nèi)部的this指向返回的對象
let res = constructor.apply(instance, args);
//如果構(gòu)造器有返回對象,則使用返回的對象整慎,沒有則使用以構(gòu)造器原型創(chuàng)建對象
return typeof res === "object" ? res : instance;
}
測試代碼
let mFunc = myNew(Foo, "張三");
mFunc.work();
console.log(mFunc.name);
二、call/apply方法
call語法
fun.call(thisArg, arg1, arg2, ...)
thisArg
在 fun 函數(shù)運行時指定的 this 值围苫。
if(thisArg == undefined|null) this = window裤园,
if(thisArg == number|boolean|string) this == new Number()|new Boolean()| new String()
arg1, arg2, ...
指定的參數(shù)列表。
返回值
使用調(diào)用者提供的 this
值和參數(shù)調(diào)用該函數(shù)的返回值剂府。若該方法沒有返回值拧揽,則返回 undefined
。
apply語法
func.apply(thisArg, [argsArray])
thisArg
可選的。在 func
函數(shù)運行時使用的 this
值淤袜。請注意痒谴,this
可能不是該方法看到的實際值:如果這個函數(shù)處于非嚴(yán)格模式下,則指定為 null
或 undefined
時會自動替換為指向全局對象铡羡,原始值會被包裝积蔚。
argsArray
可選的。一個數(shù)組或者類數(shù)組對象蓖墅,其中的數(shù)組元素將作為單獨的參數(shù)傳給 func
函數(shù)库倘。如果該參數(shù)的值為 null
或 undefined
,則表示不需要傳入任何參數(shù)论矾。從ECMAScript 5 開始可以使用類數(shù)組對象教翩。
其實call和apply的區(qū)別就是第二個參數(shù),apply第二個參數(shù)是一個數(shù)組或者類數(shù)組贪壳。其他的和call一樣饱亿,性能也一樣。所以闰靴,自定義功能時彪笼,唯一的區(qū)別就是傳遞的參數(shù)。
根據(jù)上面的條件蚂且,我們就可以寫代碼了
Function.prototype.MyCall = function(context, ...args) {
//獲取當(dāng)前this(調(diào)用MyCall的函數(shù))配猫,自定義的call方法在函數(shù)的原型對象上,所以typeof this =='function'
let mFunc = this;
//創(chuàng)建變量杏死,作為指定this對象(call的第一個參數(shù)context)的屬性名
let fnKey = "fnKey";
//把當(dāng)前this(調(diào)用MyCall的函數(shù))作為屬性值保存到指定this對象(call的第一個參數(shù)context)上泵肄,屬性名為上面定義的fnKey
context[fnKey] = mFunc;
//調(diào)用指定this對象(call的第一個參數(shù)context)上的當(dāng)前this(調(diào)用MyCall的函數(shù))
let result = context[fnKey](...args);
//執(zhí)行結(jié)束,刪除該屬性
delete context[fnKey];
return result;
};
Function.prototype.MyApply = function(context, args) {
//獲取當(dāng)前this(調(diào)用MyCall的函數(shù))淑翼,自定義的call方法在函數(shù)的原型對象上腐巢,所以typeof this =='function'
let mFunc = this;
//創(chuàng)建變量,作為指定this對象(call的第一個參數(shù)context)的屬性名
let fnKey = "fnKey";
//把當(dāng)前this(調(diào)用MyCall的函數(shù))作為屬性值保存到指定this對象(call的第一個參數(shù)context)上玄括,屬性名為上面定義的fnKey
context[fnKey] = mFunc;
//調(diào)用指定this對象(call的第一個參數(shù)context)上的當(dāng)前this(調(diào)用MyCall的函數(shù))
let result = context[fnKey](...args);
//執(zhí)行結(jié)束冯丙,刪除該屬性
delete context[fnKey];
return result;
};
測試代碼
let obj = {
name: "張三",
id: 1002
};
function TestFunc(name, id) {
this.name = name;
this.id = id;
return {
name: this.name,
id: this.id
};
}
let resCall = TestFunc.MyCall(obj, "abc", 11111);
console.log(obj);
console.log(resCall);
let resApply = TestFunc.MyApply(obj, ["efg", 2222]);
console.log(obj);
console.log(resApply);
三、Object.create()方法
語法
Object.create(proto[, propertiesObject])
proto
新創(chuàng)建對象的原型對象
propertiesObject
可選遭京。如果沒有指定為 undefined
胃惜,則是要添加到新創(chuàng)建對象的可枚舉屬性(即其自身定義的屬性,而不是其原型鏈上的枚舉屬性)對象的屬性描述符以及相應(yīng)的屬性名稱哪雕。這些屬性對應(yīng)Object.defineProperties()的第二個參數(shù)船殉。
返回新的對象,帶著指定的原型對象和屬性热监。
這里偷個懶捺弦,給新對象添加屬性就采用Object.defineProperties()
饮寞,不熟悉Object.defineProperties()
可以點擊上面藍(lán)色部分查看孝扛,里面也有兼容版本polyfill的實現(xiàn)列吼。
根據(jù)上面的條件,我們就可以寫代碼了
Object.prototype.myCreate = function(proto, propertiesObject) {
//判斷傳入的原型對象
if (typeof proto !== "object" && typeof proto !== "function") {
throw new TypeError("Object prototype may only be an Object");
} else if (proto === null) {
throw new Error(" Object.create不支持第一個參數(shù)為null");
}
if (propertiesObject === null) {
throw new Error(" Object.create第二個參數(shù)不能為null");
}
//創(chuàng)建一個構(gòu)造函數(shù)
function F() {}
//設(shè)置構(gòu)造函數(shù)的原型為新創(chuàng)建對象的原型對象
F.prototype = proto;
//構(gòu)造器的原型對象的constructor指向自身苦始,防止原型鏈錯亂
F.prototype.constructor = F;
//創(chuàng)建實例對象
let newObj = new F();
//加到新創(chuàng)建對象的可枚舉屬性
if (typeof propertiesObject != "undefined") {
//這里偷懶,這個API暫時就不自定義
Object.defineProperties(newObj, propertiesObject);
}
return newObj;
};
測試代碼
let obj = {};
let propertiesObj = {
property1: {
value: true,
writable: true
},
property2: {
value: "Hello",
writable: false
}
};
console.log(Object.create(obj, propertiesObj));
let myObj = Object.myCreate(obj, propertiesObj);
console.log(myObj);
四寞钥、數(shù)組的map方法
語法
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg])
callback
生成新數(shù)組元素的函數(shù),使用三個參數(shù):
currentValue
callback 數(shù)組中正在處理的當(dāng)前元素陌选。
index可選
callback 數(shù)組中正在處理的當(dāng)前元素的索引理郑。
array可選
callback map 方法被調(diào)用的數(shù)組。
thisArg可選
執(zhí)行 callback 函數(shù)時使用的this 值咨油。
返回一個新數(shù)組您炉,每個元素都是回調(diào)函數(shù)的結(jié)果。
根據(jù)上面的條件役电,我們就可以寫代碼了
Array.prototype.MyMap = function (callBack, context) {
// 淺拷貝要遍歷的數(shù)組
var copyArr = Array.prototype.slice.call(this);
//有就使用赚爵,null或者undefined,設(shè)定為全局對象
context = context || window;
//調(diào)函數(shù)的結(jié)果
var resultArr = [];
for (let index = 0; index < copyArr.length; index++) {
//是否是自己的屬性
if (!copyArr.hasOwnProperty(index)) continue;
//把回調(diào)結(jié)果放入返回的數(shù)組中法瑟,回調(diào)函數(shù)按照上面設(shè)置為三個參數(shù)
resultArr.push(callBack.call(context, copyArr[index], index, this));
}
return resultArr;
}
測試代碼
let arr = [1, 2, { name: '123' }];
arr.MyMap(function(item,index,context){
console.log('item:'+item,"index:"+index);
console.log(this);
},{id:123});
let arr = [1, 2, { name: '123' }];
let newArr= arr.MyMap(function(item,index,context){
console.log('item:'+item,"index:"+index);
console.log(this);
return {name:item}
});
console.log(newArr);
另外冀膝,map方法中,對于每一項是淺拷貝霎挟。
let arr = [1, 2, { name: '123' }];
arr.map(function (item,index) {
if (typeof item === 'object') {
item.id = 4555;
} else {
item = index;
}
});
console.log(arr);
所以窝剖,上面采用淺拷貝就行,淺拷貝和深拷貝有疑惑的酥夭,可以閱讀一下這篇文章
五赐纱、數(shù)組的reduce方法
語法
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
callback
執(zhí)行數(shù)組中每個值的函數(shù),包含四個參數(shù):
accumulator
累計器累計回調(diào)的返回值; 它是上一次調(diào)用回調(diào)時返回的累積值采郎,或initialValue(見于下方)千所。
currentValue
數(shù)組中正在處理的元素。
currentIndex可選
數(shù)組中正在處理的當(dāng)前元素的索引蒜埋。 如果提供了initialValue淫痰,則起始索引號為0,否則為1整份。
array可選
調(diào)用reduce()的數(shù)組
initialValue可選
作為第一次調(diào)用 callback函數(shù)時的第一個參數(shù)的值待错。 如果沒有提供初始值,則將使用數(shù)組中的第一個元素烈评。 在沒有初始值的空數(shù)組上調(diào)用 reduce 將報錯火俄。
返回函數(shù)累計處理的結(jié)果。
根據(jù)上面的條件讲冠,我們就可以寫代碼了
Array.prototype.myReduce = function (callBack, initialValue) {
// 淺拷貝要遍歷的數(shù)組
let copyArr = Array.prototype.slice.call(this);
//提供初始值瓜客,從第一項開始,沒有提供從第二項開始
let currentIndex = initialValue ? 0 : 1;
let result = initialValue ? initialValue : copyArr[0];
for (let index = currentIndex; index < copyArr.length; index++) {
//調(diào)用回調(diào)函數(shù),按照上面的設(shè)置四個參數(shù)
result = callBack.call(null, result, copyArr[index], index, this);
}
return result;
}
下面是測試代碼
let redarr = [1, 2, 3, 4, 5];
let result = redarr.reduce(function (accumulator, item) {
return accumulator + item;
}, 10);
console.log(result);
let mResult = redarr.myReduce(function (accumulator, item) {
return accumulator + item;
return
}, 10);
console.log(mResult);
bind方法
語法
function.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
調(diào)用綁定函數(shù)時作為this
參數(shù)傳遞給目標(biāo)函數(shù)的值谱仪。 如果使用new
運算符構(gòu)造綁定函數(shù)玻熙,則忽略該值。當(dāng)使用bind
在setTimeout
中創(chuàng)建一個函數(shù)(作為回調(diào)提供)時疯攒,作為thisArg
傳遞的任何原始值都將轉(zhuǎn)換為object
嗦随。如果bind
函數(shù)的參數(shù)列表為空,執(zhí)行作用域的this
將被視為新函數(shù)的thisArg
敬尺。
arg1, arg2, ...
當(dāng)目標(biāo)函數(shù)被調(diào)用時,預(yù)先添加到綁定函數(shù)的參數(shù)列表中的參數(shù)砂吞。
返回一個原函數(shù)的拷貝,并擁有指定的this值和初始參數(shù)蜻直。
根據(jù)上面的條件,我們就可以寫代碼了
Function.prototype.myBind = function(context, ...args) {
//獲取當(dāng)前方法
let _that = this;
//創(chuàng)建返回的函數(shù)
let mNewFunc = function(...argsNew) {
//把參數(shù)拼接袭蝗,并改變原函數(shù)的this對象,返回原函數(shù)的返回值
return _that.apply(context || window, [...args, ...argsNew]);
};
//保證原函數(shù)的原型對象上的屬性不丟失
mNewFunc.prototype = Object.create(_that.prototype);
return mNewFunc;
};
測試代碼
var myName = "999";
let module = {
myName: "上海"
};
function bindFuc(args) {}
bindFuc.myName = "ABCDE";
function textFuc(args) {
console.log("args:", arguments);
console.log("myName:" + this.myName);
}
var boundTextFuc = textFuc.bind(module, "123");
boundTextFuc("145");
var boundTextFuc = textFuc.myBind(module, "123");
boundGetX("145");
instanceof運算符
語法
object instanceof constructor
object
要檢測的對象
constructor
某個構(gòu)造函數(shù)
instanceof 運算符用來檢測 constructor.prototype 是否存在于參數(shù) object 的原型鏈上朵逝。
根據(jù)上面的條件,我們就可以寫代碼了
Object.prototype.myInstanceof = function(constructor) {
//獲取要檢測的對象
let object = this;
//判斷constructor是否是對象
if (constructor == null || typeof constructor !== "function") {
return new Error("第二個參數(shù)必須為構(gòu)造函數(shù)");
}
//獲取驗檢測對象的原型
let objProto = Object.getPrototypeOf(object);
//循環(huán)在檢測對象的原型鏈上查找是否存在constructor.prototype
while (true) {
//排除檢測對象是Object.prototype的原型的情況
if (objProto == null) return false;
if (objProto == constructor.prototype) return true;
objProto = Object.getPrototypeOf(objProto);
}
};
測試代碼
function TestFunc() {}
let mTestFunc = new TestFunc();
console.log(mTestFunc.myInstanceof(Number),mTestFunc instanceof Number);
console.log(mTestFunc.myInstanceof(Function),mTestFunc instanceof Function);
console.log(mTestFunc.myInstanceof(Object),mTestFunc instanceof Object);
console.log(Function.myInstanceof(Object),Function instanceof Object);
console.log(Object.myInstanceof(Function),Object instanceof Function);