instanceof
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上
js
function myInstanceof(left, right) {
// 基本类型直接返回 false
if (
left === null ||
(typeof left !== "object" && typeof left !== "function")
) {
return false;
}
// 获取对象的原型
let proto = Object.getPrototypeOf(left);
while (true) {
// 到达原型链末端
if (proto === null) return false;
// 找到匹配的原型对象
if (proto === right.prototype) return true;
// 继续向上遍历原型链
proto = Object.getPrototypeOf(proto);
}
}
console.log(myInstanceof([], Array)); // true
console.log(myInstanceof({}, Object)); // true
console.log(myInstanceof(123, Number)); // false (基本类型不适用)
bind
- bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,
- 还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。
- 如果目标函数是可构造的,绑定函数也可以使用 new 运算符进行构造。这样做的效果就好像目标函数本身被构造一样。前置的参数会像通常一样传递给目标函数,而提供的 this 值会被忽略(因为构造函数会准备自己的 this)
js
Function.prototype.myBind = function (NThis) {
// 要绑定的函数
var FToBind = this;
/**
* arguments 是一个对应于传递给函数的参数的类数组对象,有 长度 属性 并且属性的索引是从零开始的,但是它没有 Array的 内置方法
* arguments对象是所有(非箭头)函数中都可用的局部变量
* Array.from(arguments) / [...arguments]
*/
var args = Array.prototype.slice.call(arguments, 1);
// new
var FNop = function () {};
/**
* new 流程
* 1. 创建一个新的对象
* 2. 如果构造函数的 prototype 属性是一个对象,则将 新对象 的 [[Prototype]] 指向构造函数的 prototype 属性,否则 新对象 将保持为一个普通对象,其 [[Prototype]] 为 Object.prototype。
* 3. this 绑定到 这对象执行函数
* 4. 如果构造函数返回非原始值,则该返回值成为整个 new 表达式的结果。否则,如果构造函数未返回任何值或返回了一个原始值,则返回 新对象。(通常构造函数不返回值,但可以选择返回值,以覆盖正常的对象创建过程。)
*/
// 已经绑定了的函数
FBound = function bound() {
FToBind.apply(
this instanceof FNop ? this : NThis,
args.concat(Array.prototype.slice.call(arguments))
);
};
FNop.prototype = this.prototype;
FBound.prototype = new FNop();
return FBound;
};
Function.prototype.myBind2 = function (NThis, ...args) {
// 要绑定的函数
const FToBind = this;
// new
const FNop = function () {};
// 已经绑定了的函数
const FBound = function bound(...rest) {
FToBind.apply(
FBound.prototype.isPrototypeOf(this) ? this : NThis,
args.concat(rest)
);
};
FNop.prototype = this.prototype;
FBound.prototype = new FNop();
return FBound;
};
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: "Alice" };
// 基础绑定
const boundGreet = greet.myBind(person, "Hello");
boundGreet("!"); // 输出: "Hello, Alice!"
// new 调用测试
function Person(name) {
this.name = name;
}
const BoundPerson = Person.myBind({});
const obj = new BoundPerson("Bob");
console.log(obj.name); // 输出: "Bob"(证明 new 生效)
console.log(obj instanceof Person); // 输出: true(原型链正确)
柯里化
js
function curry(fn, ...args) {
// 函数参数长度
const pLength = fn.length;
return function curryFn(...rest) {
const params = [...args, ...rest];
// 大于等于参数数量
if (params.length >= pLength) {
return fn.apply(this, params);
} else {
return curry.call(this, fn, ...params);
}
};
}
function curry2(fn) {
const pLength = fn.length;
return function curryFn(...rest) {
if (rest.length >= pLength) {
return fn.apply(this, rest);
} else {
return function subFn(...subRest) {
return curryFn.call(this, ...rest, ...subRest)
}
}
}
}
const add = (a, b, c) => a + b + c;
const curriedAdd = curry2(add);
// 测试不同调用方式
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
console.log(curriedAdd(1, 2, 3)); // 6
array 扁平化
js
/**
* 数组扁平化
*/
const arr = [0, 1, [2, [3, [4, 5]]]];
console.log(arr.flat()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.flat(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.flat(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.flat(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.flat(4)); // [ 0, 1, 2, 3, 4, 5 ]
Array.prototype.myFlat2 = function myFlat2(depth = 1) {
const res = [];
(function flatten(source, depth) {
for (const item of source) {
if (Array.isArray(item) && depth > 0) {
flatten(item, depth - 1);
} else {
res.push(item);
}
}
})(this, depth);
return res;
};
console.log(arr.myFlat2()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.myFlat2(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.myFlat2(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.myFlat2(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.myFlat2(4)); // [ 0, 1, 2, 3, 4, 5 ]
Array.prototype.myFlat3 = function myFlat3(depth = 1) {
return depth > 0
? this.reduce((pre, cur) => {
return Array.isArray(cur)
? pre.concat(cur.myFlat3(depth - 1))
: pre.concat(cur);
}, [])
: this.slice();
};
console.log(arr.myFlat3()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.myFlat3(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.myFlat3(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.myFlat3(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.myFlat3(4)); // [ 0, 1, 2, 3, 4, 5 ]
Array.prototype.myFlat4 = function myFlat4(depth = 1) {
let arr = this.slice();
while (arr.some((item) => Array.isArray(item) && depth > 0)) {
arr = [].concat(...arr);
depth--;
}
return arr;
};
console.log(arr.myFlat4()); // [ 0, 1, 2, [ 3, [ 4, 5 ] ] ]
console.log(arr.myFlat4(0)); // [ 0, 1, [ 2, [ 3, [Array] ] ] ]
console.log(arr.myFlat4(2)); // [ 0, 1, 2, 3, [ 4, 5 ] ]
console.log(arr.myFlat4(3)); // [ 0, 1, 2, 3, 4, 5 ]
console.log(arr.myFlat4(4)); // [ 0, 1, 2, 3, 4, 5 ]
/**
* 不考虑 depth
*/
/**
* 数组的 toString 方法实际上在内部调用了 join() 方法来拼接数组并返回一个包含所有数组元素的字符串,元素之间用逗号分隔。
*/
console.log(arr.toString()); // 0,1,2,3,4,5
console.log(arr.toString().split(",").map(Number)); // [ 0, 1, 2, 3, 4, 5 ]
/**
* join 为什么会展开数据
* https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.join
*/
Array.prototype.myJoin = function myJoin(symbol = ",") {
let result = "";
let k = 0;
const len = this.length;
while (k < len) {
if (k > 0) {
result += symbol;
}
/**
* 这里 数组的 toString 方法实际上在内部调用了 join()
*/
result += this[k].toString();
k++;
}
return result;
};
console.log(arr.myJoin()); // 0,1,2,3,4,5
console.log(arr.myJoin().split(",").map(Number)); // [ 0, 1, 2, 3, 4, 5 ]