- new 模拟实现
- bind 模拟实现
- call 模拟实现
- apply 模拟实现
- 数组去重
new 模拟实现
- 创建一个新对象
- 这个新对象会执行
[[prototype]]连接 - 这个新对象会绑定到函数调用的
this - 如果函数没有返回对象,new表达式中的函数调用会自动返回这个新对象
function create() {
// 创建一个空对象
var obj = new Object();
// 获取外部传入的构造函数,同时删除
var Constructor = Array.prototype.shift.call(arguments);
// 实现继承,obj可以访问到构造函数中的属性
obj.__proto__ = Constructor.prototype;
// 调用构造函数,并且改变this指向
var ret = Constructor.apply(obj, arguments);
// 优先返回构造函数返回的对象
return ret instanceof Object ? ret : obj;
}
bind 模拟实现
bind()会创建一个新函数,当新函数被调用时,它的this会指向bind()的第一个参数。bind和apply/call的区别是,前者返回了一个绑定上下文的函数,后者是直接执行了函数。
Function.prototype.mybind = function(thisArg) {
if (typeof this !== 'function') {
throw TypeError("Bind must be called on a function");
}
// 拿到参数,为了传给调用者
const args = Array.prototype.slice.call(arguments, 1),
// 保存 this
self = this,
// 构建一个干净的函数,用于保存原函数的原型
nop = function() {},
// 绑定的函数
bound = function() {
// this instanceof nop, 判断是否使用 new 来调用 bound
// 如果是 new 来调用的话,this的指向就是其实例,
// 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
return self.apply(
this instanceof nop ? this : thisArg,
args.concat(Array.prototype.slice.call(arguments))
);
};
// 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
if (this.prototype) {
nop.prototype = this.prototype;
}
// 修改绑定函数的原型指向
bound.prototype = new nop();
return bound;
}
}
call 模拟实现
call可以接收一个参数列表
Function.prototype.myCall = function (thisArgs) {
if (typeof this !== 'function') {
throw new TypeError('类型错误')
}
const fn = Symbol('fn');
const args = [...arguments].slice(1);
thisArgs[fn] = this;
var result = thisArgs[fn](...args);
delete thisArgs[fn];
return result;
};
function foo() {
console.log(this.name);
console.log([...arguments]);
}
var obj = {
name: 'Lusen'
};
foo.myCall(obj, 1, 2);
apply 模拟实现
apply只能接收两个参数,第二个参数为数组
Function.prototype.myApply = function (thisArgs) {
if (typeof this !== 'function') {
throw new TypeError('类型错误')
}
const fn = Symbol('fn');
const args = arguments[1];
thisArgs[fn] = this;
var result = thisArgs[fn](...args);
delete thisArgs[fn];
return result;
};
function foo() {
console.log(this.name);
console.log([...arguments]);
}
var obj = {
name: 'Lusen'
};
foo.myApply(obj, [1, 2, 3]);
call/apply使用场景:
- 合并两个数组
var arr1 = [1, 2];
var arr2 = [3, 4];
Array.prototype.push.apply(arr1, arr2);
- 获取数组中最大值
var arr1 = [1, 2, 3, 4, 5];
Math.max.apply(Math, arr1); // 5
Math.max.call(Math, -2, 22, 11); // 22
// ES6
Math.max.call(Math, ...arr1) // 5
- 验证是否是数组
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[Object Array]';
}
- 类数组转化
function transData(obj) {
return Array.prototype.slice.call(obj);
}
- 调用父构造函数实现继承
function Super() {
this.color = ['red', 'yellow'];
}
function Sub() {
Super.call(this);
}
数组去重
- 利用ES6提供的新的数据结构Set,它类似于数组,但成员值都是唯一的。(常用,效率高)
function unique(arr) {
// return [...new Set(arr)];
return Array.from(new Set(arr));
}
- for循环嵌套for,然后splice去重
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('类型错误');
}
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
}
- 利用indexOf判断
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('类型错误');
}
let result = [];
for (let i = 0; i < arr.length; i++) {
if (result.indexOf(arr[i]) === -1) {
result.push(arr[i]);
}
}
return result;
}
- 利用includes判断
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('类型错误');
}
let result = [];
for (let i = 0; i < arr.length; i++) {
if (!result.includes(arr[i])) {
result.push(arr[i]);
}
}
return result;
}
- 利用reduce + includes
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('类型错误');
}
return arr.reduce((prev, cur) => {
return prev.includes(cur) ? prev : [...prev, cur]
}, []);
}
- 利用filter + indexOf
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('类型错误');
}
return arr.filter((item, index) => {
return arr.indexOf(item) === index;
})
}
- for of + Object 利用对象的属性不可重复这一特性,效率比
new Set(...)高
function unique(arr) {
if (!Array.isArray(arr)) {
throw new TypeError('类型错误');
}
let result = [],
obj = {};
for (let i of arr) {
if (!obj[i]) {
result.push(i);
obj[i] = 1;
}
}
return result;
}