AMD CMD CommonJS
/* AMD是RequireJS對模塊化的定義
* CMD是seaJS對模塊化的定義
* CommonJS是Node對模塊化的規(guī)范
**/
/* AMD 依賴關(guān)系前置 */
define(['./a', './b'], function (a, b) {
a.something();
b.something();
})
/* CMD 按需加載,依賴就近 */
define(function (require, exports, module) {
var a = require('./a');
a.something();
var b = require('./b');
b.something();
})
阻止默認(rèn)行為和阻止冒泡
function stopDefault( e ) {
// 阻止默認(rèn)瀏覽器動(dòng)作(W3C)
if ( e && e.preventDefault ) {
e.preventDefault();
} else {
// IE中阻止函數(shù)器默認(rèn)動(dòng)作的方式
window.event.returnValue = false;
}
return false;
}
function stopBubble(e) {
// 如果提供了事件對象,則這是一個(gè)非IE瀏覽器
if ( e && e.stopPropagation ) {
// 因此它支持W3C的stopPropagation()方法
e.stopPropagation();
} else {
// 否則,我們需要使用IE的方式來取消事件冒泡
window.event.cancelBubble = true;
}
}
JS數(shù)組
slice實(shí)現(xiàn)淺拷貝
var arr = ['old', 1, true, null, undefined];
var new_arr = arr.slice();
new_arr[0] = 'new';
console.log(arr) // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined]
concat實(shí)現(xiàn)淺拷貝
var arr = ['old', 1, true, null, undefined];
var new_arr = arr.concat();
new_arr[0] = 'new';
console.log(arr) // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined]
以上兩種方法只是淺拷貝,如果數(shù)組元素是基本類型,就會(huì)拷貝一份新的;但是如果數(shù)組元素是對象或者數(shù)組,就只會(huì)拷貝引用(類似指針),修改其中一個(gè)就會(huì)影響另外一個(gè)。
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}];
var new_arr = arr.concat();
new_arr[0] = 'new';
new_arr[3][0] = 'new1';
console.log(arr) // ["old", 1, true, ['new1', 'old2'], {old: 1}]
console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}]
JSON.stringify 實(shí)現(xiàn)數(shù)組深拷貝
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}];
var new_arr = JSON.parse(JSON.stringify(arr));
new_arr[0] = 'new';
new_arr[3][0] = 'new1';
console.log(arr) // ["old", 1, true, ['old1', 'old2'], {old: 1}]
console.log(new_arr) // ["new", 1, true, ['new1', 'old2'], {old: 1}]
手動(dòng)實(shí)現(xiàn)深淺拷貝
/*淺拷貝*/
var shallowCopy = function (obj) {
// 判斷是否是數(shù)組或者對象
if (typeof obj !== 'object') {
return
}
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
/*深拷貝*/
var deepCopy = function (obj) {
if (typeof obj !== 'object') {
return
}
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj
}
數(shù)組去重
/* filter + indexOf */
function unique (arr) {
var res = arr.filter(function (item, index, array) {
return array.indexOf(item) === index;
})
return res;
}
/* ES6 */
function unique (arr) {
return [... new Set(arr)];
}
打亂數(shù)組的方法
var arr = [];
for (var i = 0; i < 100; i++) {
arr[i] = i;
}
arr.sort(function () {
return 0.5 - Math.random();
});
數(shù)組扁平化
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(arr))
柯里化
執(zhí)行add(1,2,3)(2)()就能輸出1+2+3+2=8。函數(shù)柯里化
const adder = function () {
let _args = []; // 閉包保留了對 _args 的引用
return function () {
console.log(arguments)
if (arguments.length === 0) {
return _args.reduce(function (a, b) {
return a + b;
});
}
[].push.apply(_args, arguments);
return arguments.callee;
}
};
const sum = adder();
console.log(sum); // Function
sum(100, 200)(300); // 調(diào)用形式靈活,一次調(diào)用可輸入一個(gè)或者多個(gè)參數(shù),并且支持鏈?zhǔn)秸{(diào)用
sum(400);
console.log(sum()); // 1000 (加總計(jì)算)
異步函數(shù)
由async關(guān)鍵詞定義的函數(shù)聲明了一個(gè)可以異步執(zhí)行的函數(shù),返回一個(gè)AsyncFunction類型的對象。淺談Async/Await
async
async—聲明一個(gè)異步函數(shù) (async function someName() {...})
- 自動(dòng)將常規(guī)函數(shù)轉(zhuǎn)換成
Promise,返回值也是一個(gè)Promise對象; - 只有
async函數(shù)內(nèi)部的異步操作執(zhí)行完,才會(huì)執(zhí)行then方法指定的回調(diào)函數(shù); - 異步函數(shù)內(nèi)部可以使用
await。
await
await—暫停異步的功能執(zhí)行(var result = await someAsyncCall() {...})
- 放置在
Promise調(diào)用之前,await強(qiáng)制其他代碼等待,直到Promise完成并返回結(jié)果; - 只能與
Promise一起使用,不適用與回調(diào); - 只能在
async函數(shù)內(nèi)部使用。
function fetchTextByPromise () {
return new Promise(resolve => {
setTimeout(() => {
resolve('es8');
}, 2000);
});
}
async function sayHello () {
const externalFetchedText = await fetchTextByPromise();
console.log(`Hello, ${externalFetchedText}`);
}
sayHello();
bind,call,apply
call
call()方法調(diào)用一個(gè)函數(shù), 其具有一個(gè)指定的this值和分別地提供的參數(shù)(參數(shù)的列表)。
thisArg
- 在
fun函數(shù)運(yùn)行時(shí)指定的this值。需要注意的是,指定的this值并不一定是該函數(shù)執(zhí)行時(shí)真正的this值; - 如果這個(gè)函數(shù)處于非嚴(yán)格模式下,則指定為
null和undefined的this值會(huì)自動(dòng)指向全局對象(瀏覽器中就是window對象); - 值為原始值(數(shù)字,字符串,布爾值)的
this會(huì)指向該原始值的自動(dòng)包裝對象。
arg1, arg2, ...
- 指定的參數(shù)列表。
var foo = {
value: 'fooValue'
}
function bar() {
var value = 'barValue'
console.log(this.value)
}
bar.call(foo) // 'fooValue'
call的polyfill
- 將函數(shù)設(shè)為對象的屬性
- 執(zhí)行該函數(shù)
- 刪除該函數(shù)
/*
* 將函數(shù)設(shè)為對象的屬性 foo.bar
* 執(zhí)行該函數(shù) foo.bar()
* 刪除該函數(shù) delete foo.bar
**/
Function.prototype.myCall = function (con) {
var context = con || window
var args = []
for (var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']')
}
context.fn = this
var result = eval('context.fn(' + args + ')')
delete context.fn
return result
}
apply
call()方法的作用和 apply() 方法類似,區(qū)別就是call()方法接受的是參數(shù)列表,而apply()方法接受的是一個(gè)參數(shù)數(shù)組。
apply的polyfill
Function.prototype.myApply = function (con, arr) {
var context = con || window
context.fn = this
var result
if (!arr) {
result = context.fn()
} else {
var args = []
for (var i = 0; i < arr.length; i++) {
args.push(arr[i])
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result
}
bind
fun.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg
- 調(diào)用綁定函數(shù)時(shí)作為this參數(shù)傳遞給目標(biāo)函數(shù)的值。 如果使用
new,new運(yùn)算符創(chuàng)建一個(gè)用戶定義的對象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對象的實(shí)例。運(yùn)算符構(gòu)造綁定函數(shù),則忽略該值。 - 當(dāng)使用
bind在setTimeout中創(chuàng)建一個(gè)函數(shù)(作為回調(diào)提供)時(shí),作為thisArg傳遞的任何原始值都將轉(zhuǎn)換為object。 - 如果沒有提供綁定的參數(shù),則執(zhí)行作用域的
this被視為新函數(shù)的thisArg。
arg1, arg2, ...
當(dāng)綁定函數(shù)被調(diào)用時(shí),這些參數(shù)將置于實(shí)參之前傳遞給被綁定的方法。
bind的polyfill
Function.prototype.myBind = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable")
}
var self = this
var args = Array.prototype.slice.call(arguments, 1)
var fNOP = function () {}
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments)
self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs))
}
fNOP.prototype = this.prototype
fBound.prototype = new fNOP()
return fBound
}
詳解:JavaScript 之 call和apply,bind 的模擬實(shí)現(xiàn)
new
- 新生成了一個(gè)對象;
- 鏈接到原型;
- 綁定
this; - 返回新對象。
new的polyfill
function objectFactory() {
var obj = new Object()
var constructor = [].shift.call(arguments)
obj.__proto__ = constructor.prototype
console.log(obj)
var result = constructor.apply(obj, arguments)
console.log(result)
return typeof result === 'object' ? result : obj
// 判斷返回的值是不是一個(gè)對象,如果是一個(gè)對象,我們就返回這個(gè)對象,如果不是,我們該返回什么就返回什么。
}
instanceof
instanceof可以正確的判斷對象的類型,因?yàn)閮?nèi)部機(jī)制是通過判斷對象的原型鏈中是不是能找到類型的 prototype。
instanceof的polyfill
function myInstanceOf(left, right) {
// 獲得類型的原型
var prototype = right.prototype
// 獲得對象的原型
left = left.__proto__
// 判斷對象的類型是否等于類型的原型
while (true) {
if (left === null)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
檢驗(yàn)類型的方法
千萬不要使用typeof來判斷對象和數(shù)組,因?yàn)檫@種類型都會(huì)返回object。
- typeof:是判斷基本類型的
Boolean,Number,symbol,undefined,String,Symbol。對于引用類型:除function,都返回object,null返回object。 - instanceof:用來判斷
A是否是B的實(shí)例,instanceof檢查的是原型。 - toString:是
Object的原型方法,對于Object對象,直接調(diào)用toString()就能返回[Object Object]。而對于其他對象,則需要通過call/apply來調(diào)用才能返回正確的類型。