实现 Array.isArray

1
2
3
4
5
6
//思路  利用Object.prototype.toString.call()

//实现
Array.prototype.myIsArray = function(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}

实现 Object.create()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//思路Object.create方法是不继承原型链上的属性和方法

//纯净的o = {a : 1}对象, 无任何继承原型链(toString、hasOwnProperty、...)
var o = Object.create(null, {
a:{
writable:true,
configurable:true,
value:'1'
}
})

//和var o = {a: 1} 类似, 但是多了一层__proto__
var o = Object.create({},{
a:{
writable:true,
configurable:true,
value:'1'
}
})

//等于 var o = {a: 1}
var o = Object.create(Object.prototype,{
a:{
writable:true,
configurable:true,
value:'1'
}
})


//自己实现Object.create()
if(typeof Object.create !== 'function'){
Object.create = function(o){
let F = function(){};
F.prototype = o; //F原型链指向o
return new F(); //返回创建的新对象
}
}

实现 new F()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//思路
1. 创建一个空对象
2. 将空对象指向和构造函数原型
3. 以空对象为this上下文且执行构造函数
4. 返回构造函数结果

//实现
function creatObject(fn){
// 创建一个空对象,并将空对象的原型设置为构造函数的原型
let obj = Object.create(fn.prototype);

// 以obj为上下文执行构造函数
let result = fn.call(obj);

// 如果构造函数有返回结果,则返回,若无直接返回obj
if(typeof result === 'object'){
return result;
}else{
return obj;
}
}

//使用
function Person(){
this.name = 'cha';
this.age = 10;
}

//new 实现
//var res = new Person();

//模拟实现
var res = creatObject(Person);

实现 call、apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//思路
1. 改变函数this的指向
2. 执行函数


//实现
Function.prototype.myCall = function(context){
context = context || window;

//将我们传入函数挂载到context上
context.fn = this;

//处理传入参数,除去第一位,转换为数组
//console.log([...[2, 3, 4]]) ==> [2, 3, 4]
const args = [...arguments].slice(1);

//执行函数 context.fn = this, 参数数组转换为多个字符串
//console.log(...[2, 3, 4]) ==> 2, 3, 4
const result = context.fn(...args);

//删除对象上属性
delete context.fn;

//将结果返回
return result;
}


//和call不相同的地方就是参数
Function.prototype.apply = function(context){
context = context || window;

context.fn = this;

let result;

if(arguments[1]){
//console.log(...[2, 3, 4]) ==> 2, 3, 4
result = context.fn(...arguments[1]);
}else{
result = context.fn();
}

delete context.fn;

return result;
}

实现 bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//思路
1. 用bind改变this指向的函数
2. 如果用new操作符调用会失效


//实现
Function.prototype.myBind = function(){
//保存函数
const _this = this;

//保存目标函数
const context = arguments[0] || window;

//保存函数中除了目标函数的其他参数
const rest = Array.prototype.slice.call(arguments, 1);

//返回一个待执行函数
return function F(){
const rest2 = [...arguments]; //将argments转换为真正数组 Array.prototype.slice.call(arguments)

if(this instanceof F){
//判断是否通过new操作符,则直接用new 调用原函数,并用扩展运算符传递参数
return new _this(...rest2);
}else{
//用apply调用第一步保存的函数,并绑定this,传递合并的参数数组 rest.concat(rest2)
return _this.apply(context, [...rest,...rest2]);
}
}
}

实现函数柯里化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//思路
1. 参数复用,例如用于累加函数
2. 延迟执行,类似bind改变this指向,待执行函数


//实现
function myCurrying(fn, args){
let _this = this;
let len = fn.length;
let args = args || [];

return function (){
let _args = Array.prototype.slice.call(arguments);
Array.prototype.push.call(args, _args); //es6写法: args.push(..._args)/ [...args, ..._args]

// 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
if(_args.length < len){
return myCurrying.call(this, fn, _args);
}

return fn.apply(this, _args);
}
}

实现EventEmitter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//原理
1. on(event, fn) //监听event事件, 调用fn回调函数
2. once(event, fn) //指定事件注册单个监听器
3. emit(event, arg1, arg2, arg3...) //触发event事件,
4. off(event, fn) //停止监听某个事件

//实现
class EventEmit{
constructor(){
this._events = {};
}

on(event, fn){
// 这里的数组是队列, 遵循先进先出
let callback = this._events[event] || [];
if(callback.indexOf(fn) !== -1){
callback.push(fn);
}
this._events[event] = callback;
return this;
}

off(event, fn){
let callback = this._events[event];
this._events[event] = callback && callback.filter(fn => {
fn !== callback;
})
return this;
}

emit(...args){
let event = args[0];
let params = [].slice.call(args, 1); //除了event的参数==[...args].slice(1);
let callback = this._events[event];
callback.forEach((fn)=>{
fn.apply(this, params);
});
return this;
}


once(event, fn){
let wrapFn = (...args) =>{
fn.apply(this, args);
this.off(event, wrapFn);
}
this.on(event, wrapFn);
return this;
}

}

实现Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//原理
1. 三种状态 pedding fulfilled rejected
2. 实例then方法(静态方法、catchfinally 没实现)
3. 链式调用

//实现
class Promise{
constructor(fn){
this.state = 'pedding';
this.value = '';
this.reason = '';

let resolve = (value) =>{
if(this.state == 'pedding'){
this.state = 'fulfilled';
this.value = value;
}
}

let reject = (value) => {
if(this.state == 'pedding'){
this.state = 'rejected';
this.reason = value;
}
}

try{
fn(resolve, reject);
}catch(e){
reject(e);
}
}

then(onFulfilled, onRejected){
switch(this.state){
case 'fulfilled':
onFulfilled(this.value);
break
case 'rejected':
onRejected(this.value);
break
}
return this; //返回自身链式调用
}
}

实现Array.prototype.flat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//原理
1. flat将多维数组转为指定深度数组
[1, 2, [3, 4, [5, 6]]].flat() ===> [1, 2, 3, 4, [5, 6]]
[1, 2, [3, 4, [5, 6]]].flat(2) ===> [1, 2, 3, 4, 5, 6]

//实现
Array.prototype.myFlat = function (deep = 1){
if(Array.isArray(this)){
let arr = [];
if(!Numnber(deep) || Number(deep) < 1){
return this;
}

this.forEach((item)=>{
if(Array.isArray(item)){
arr = arr.conact(item.myFlat(--deep));
}else{
arr.push(item);
}
})

return arr;

}else{
console.error(`请输入数组`);
}
}

实现 Array.prototype.reduce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//原理
1. Array.prototype.reduce(callback(accumulator, currentValue[, index[, array]])[,initialValue]) //参数:回调函数[accumulator累加值, currentValue当前值, index索引, array源数组](必填)、初始值(选填)
2.

//实现
Array.prototype.myReduce = function(cb, initVal){
let accumulator = initVal ? initVal : this[0]; //获取数组中带个元素当初始值
for(let i=initVal ? 0 : 1; i<this.length; i++){
accumulator = cb(accumulator, this[i], i, this);
}
return accumulator;
}

//用法
[1, 2, 3, 4].myReduce((acc, current)=>{
acc+=current;
return acc;
}, 5); //15

实现 Array.prototype.map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//原理
let newArr = arr.map(function callback(currentValue[, index[, array]){

}[, thisArg]) //参数: currentValue当前值 index索引值 array调用的函数 thisArg执行callback函数时值被用作this。

[1, 2, 3].map(parseInt) => [1, NaN, NaN] //parseInt(1, 0) parseInt(2, 1) parseInt(3, 2)

//实现
Array.prototype.myMap = function(callback, thisArg){
let arr = [];
for(let i=0; i<this.length; i++){
arr.push(callback.call(thisArg, this[i], i, this));
}
return arr;
}

实现Object.defineProperty

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//原理


//双向绑定实现
const data = {
text: 'init'
}

var input = document.getElementById('input');
var span = document.getElementById('span');

Object.defineProperty(data, 'text', {
//数据变化修改视图
set(newValue){
span.innerText = newValue;
},
get(){
alert('触发了 get');
return 'get'; //此时text = 'get'
}
})

input.addEventListener('keyup', function(e){
//触发set
data.text = e.target.value;
})

实现 new Proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//原理

//双向绑定实现
const data = {
text: 'init'
}

var input = document.getElementById('input');
var span = document.getElementById('span');

const handler = {
set(target, key, value){
target[key] = value;
span.innerText = value;
return value;
},
get(target, key){
return key in target ? target[key] : '';
}
}

const proxy = new Proxy(data,handler);

input.addEventListener('keyup', function(e){
//触发set
proxy.text = e.target.value;
})

实现ajax

1
2