AMD CMD CommonJS
/* AMD是RequireJS对模块化的定义
* CMD是seaJS对模块化的定义
* CommonJS是Node对模块化的规范
**/
/* AMD 依赖关系前置 */
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();
})
阻止默认行为和阻止冒泡
function stopDefault( e ) {
// 阻止默认浏览器动作(W3C)
if ( e && e.preventDefault ) {
e.preventDefault();
} else {
// IE中阻止函数器默认动作的方式
window.event.returnValue = false;
}
return false;
}
function stopBubble(e) {
// 如果提供了事件对象,则这是一个非IE浏览器
if ( e && e.stopPropagation ) {
// 因此它支持W3C的stopPropagation()方法
e.stopPropagation();
} else {
// 否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
}
JS数组
slice实现浅拷贝
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实现浅拷贝
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]
以上两种方法只是浅拷贝,如果数组元素是基本类型,就会拷贝一份新的;但是如果数组元素是对象或者数组,就只会拷贝引用(类似指针),修改其中一个就会影响另外一个。
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 实现数组深拷贝
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}]
手动实现深浅拷贝
/*浅拷贝*/
var shallowCopy = function (obj) {
// 判断是否是数组或者对象
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
}
数组去重
/* 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)];
}
打乱数组的方法
var arr = [];
for (var i = 0; i < 100; i++) {
arr[i] = i;
}
arr.sort(function () {
return 0.5 - Math.random();
});
数组扁平化
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))
柯里化
执行add(1,2,3)(2)()就能输出1+2+3+2=8。函数柯里化
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); // 调用形式灵活,一次调用可输入一个或者多个参数,并且支持链式调用
sum(400);
console.log(sum()); // 1000 (加总计算)
异步函数
由async
关键词定义的函数声明了一个可以异步执行的函数,返回一个AsyncFunction
类型的对象。浅谈Async/Await
async
async
—声明一个异步函数 (async function someName() {...})
- 自动将常规函数转换成
Promise
,返回值也是一个Promise
对象; - 只有
async
函数内部的异步操作执行完,才会执行then
方法指定的回调函数; - 异步函数内部可以使用
await
。
await
await
—暂停异步的功能执行(var result = await someAsyncCall() {...})
- 放置在
Promise
调用之前,await
强制其他代码等待,直到Promise
完成并返回结果; - 只能与
Promise
一起使用,不适用与回调; - 只能在
async
函数内部使用。
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()
方法调用一个函数, 其具有一个指定的this
值和分别地提供的参数(参数的列表)。
thisArg
- 在
fun
函数运行时指定的this
值。需要注意的是,指定的this
值并不一定是该函数执行时真正的this
值; - 如果这个函数处于非严格模式下,则指定为
null
和undefined
的this
值会自动指向全局对象(浏览器中就是window对象); - 值为原始值(数字,字符串,布尔值)的
this
会指向该原始值的自动包装对象。
arg1, arg2, ...
- 指定的参数列表。
var foo = {
value: 'fooValue'
}
function bar() {
var value = 'barValue'
console.log(this.value)
}
bar.call(foo) // 'fooValue'
call的polyfill
- 将函数设为对象的属性
- 执行该函数
- 删除该函数
/*
* 将函数设为对象的属性 foo.bar
* 执行该函数 foo.bar()
* 删除该函数 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()
方法类似,区别就是call()
方法接受的是参数列表,而apply()
方法接受的是一个参数数组。
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
- 调用绑定函数时作为this参数传递给目标函数的值。 如果使用
new
,new
运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。运算符构造绑定函数,则忽略该值。 - 当使用
bind
在setTimeout
中创建一个函数(作为回调提供)时,作为thisArg
传递的任何原始值都将转换为object
。 - 如果没有提供绑定的参数,则执行作用域的
this
被视为新函数的thisArg
。
arg1, arg2, ...
当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
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 的模拟实现
new
- 新生成了一个对象;
- 链接到原型;
- 绑定
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
// 判断返回的值是不是一个对象,如果是一个对象,我们就返回这个对象,如果不是,我们该返回什么就返回什么。
}
instanceof
instanceof
可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 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__
}
}
检验类型的方法
千万不要使用typeof
来判断对象和数组,因为这种类型都会返回object
。
- typeof:是判断基本类型的
Boolean
,Number
,symbol
,undefined
,String
,Symbol
。对于引用类型:除function
,都返回object
,null
返回object
。 - instanceof:用来判断
A
是否是B
的实例,instanceof
检查的是原型。 - toString:是
Object
的原型方法,对于Object
对象,直接调用toString()
就能返回[Object Object]
。而对于其他对象,则需要通过call
/apply
来调用才能返回正确的类型。