手撕
js 特性
7.自增运算符
// 1
let a = 1;
// 2 2
const b = ++a;
// 3 2 2
const c = a++;
console.log(a);
console.log(b);
console.log(c);
8.隐式转化 I
console.log(Boolean("false")); // true
console.log(Boolean(false)); // false
console.log("3" + 1); // "31" 字符串类型,打印出来还是数字
console.log("3" - 1); // 2
console.log("3" - " 02 "); // 1
console.log("3" * " 02 "); // 6
console.log(Number("1")); // 1
console.log(Number("number")); //NaN
console.log(Number(null)); // 0
console.log(Number(false)); // 0
js 实现
2.带占位符的柯里化
curriedJoin(_, 2)(1, 3); // '1_2_3'
function curry(func) {
return function curried(...args) {
const complete =
args.length >= func.length &&
!args.slice(0, func.length).includes(curry.placeholder);
if (complete) return func.call(this, ...args);
return function (...newArgs) {
// replace placeholders in args with values from newArgs
// _,_,_ + _,a -> _,_,_ + a -> a,_,_
const res = args.map((arg) =>
arg === curry.placeholder && newArgs.length ? newArgs.shift() : arg
);
return curried(...res, ...newArgs);
};
};
}
curry.placeholder = Symbol();
补充
3.实现箭头函数
babel 就是这样转译 的
es6->es5
// ES6
const obj = {
getArrow() {
return () => {
console.log(this === obj);
};
},
};
// ES5,由 Babel 转译
var obj = {
getArrow: function getArrow() {
// 继承上一层作用域的this
// 如果!getArrow在使用时,作用域被推到window上,因此上一层作用域是window
var _this = this;
return function () {
console.log(_this === obj);
};
},
};
注意是
() => {
console.log(this === obj);
};
----
var _this = this;
function () {
console.log(_this === obj);
};
4.实现私有属性
class E {
constructor() {
this.map = new Map();
this.map.set(this, { a: 1 });
}
getA() {
return this.map.get(this)["a"];
}
}
let e = new E();
console.log(e.getA());
var Person = (function () {
var privateData = new WeakMap();
function Person(name) {
privateData.set(this, { name: name });
}
Person.prototype.getName = function () {
return privateData.get(this).name;
};
return Person;
})();
用 this 做 map 上的 Key
这样在外界无法直接找到 map 上存有数据的那个键
利用 map 的键可以是对象
5.洋葱模型中间件+compose
// function middleWare({ getState, dispatch }) {
// return next => action => {
// ...
// next(action);
// }
// }
const middlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args),
};
// 向chain中每个高阶函数注入API
const chain = middlewares.map((middleware) => middleware(middlewareAPI));
// 接下来执行的是compose聚合函数,把chain中的中间件聚合成一个新的dispatch函数:
//第一个中间件是store.dispatch
const dispatch = compose(...chain)(store.dispatch);
return { ...store, dispatch };
function compose(...functions) {
if (functions.length === 0) {
return (...args) => args;
} else if (functions.length === 1) {
return functions[0];
} else {
return functions.reduce(
(pre, current) =>
(...args) =>
pre(current(args))
);
// [a,b,c]
// (...args)=>a(b(args))
// (...args)=>a(b(c))
}
}
6.实现 new
!!!prototype 不允许直接修改 不可直接被外界访问和修改
const _new = function (constructor, ...args) {
// new关键字做了4件事
// 1. 创建一个新对象
const obj = {};
// 2. 为新对象添加属性__proto__,将该属性链接至构造函 数的原型对象
obj.__proto__ = Object.create(constructor.prototype);
// 3. 执行构造函数,this被绑定在新对象上
const res = constructor.apply(obj, args);
// 4. 确保返回一个对象
return res instanceof Object ? res : obj;
};
7.Function.prototype.call/apply
Function.prototype._call = function (target = window) {
// fn可以随便起个名字,只要最后删除就行
// 注意 this 是 fn
target["fn"] = this;
//参数中去掉target
let arg = [...arguments].shift();
const result = target["fn"](...arg);
delete target["fn"];
return result;
};
Function.prototype._apply = function (target = window) {
target["fn"] = this;
let arg = [...arguments].shift();
// 其实和call是一样的,因为都先转为数组再展开
const result = target["fn"](...arg);
delete target["fn"];
return result;
};