专栏声明:只求用最简单的,容易理解的方法通过,不求优化,不喜勿喷
2693. 使用自定义上下文调用函数
题面
增强所有函数,使其具有 callPolyfill 方法。该方法接受一个对象 obj 作为第一个参数,以及任意数量的附加参数。obj 成为函数的 this 上下文。附加参数将传递给该函数(即 callPolyfill 方法所属的函数)。
例如,如果有以下函数:
function tax(price, taxRate) {
const totalCost = price * (1 + taxRate);
console.log(`The cost of ${this.item} is ${totalCost}`);
}
调用 tax(10, 0.1) 将输出 “The cost of undefined is 11” 。这是因为 this 上下文未定义。
然而,调用 tax.callPolyfill({item: “salad”}, 10, 0.1) 将输出 “The cost of salad is 11” 。this 上下文被正确设置,函数输出了适当的结果。
请在不使用内置的 Function.call 方法的情况下解决这个问题。
知识点:
apply
思路
这题考察 js 的一个 api —— apply,apply方法能劫持另外一个对象的方法,继承另外一个对象的属性。
Function.apply(obj,args) 方法能接收两个参数
obj:这个对象将代替Function类里 this 对象
args:这个是数组,它将作为参数传给Function
代码
/**
* @param {Object} context
* @param {any[]} args
* @return {any}
*/
Function.prototype.callPolyfill = function(context, ...args) {
return this.apply(context, args);
}
/**
* function increment() { this.count++; return this.count; }
* increment.callPolyfill({count: 1}); // 2
*/
2694. 事件发射器
题面
设计一个 EventEmitter 类。这个接口与 Node.js 或 DOM 的 Event Target 接口相似,但有一些差异。EventEmitter 应该允许订阅事件和触发事件。
你的 EventEmitter 类应该有以下两个方法:
subscribe - 这个方法接收两个参数:一个作为字符串的事件名和一个回调函数。当事件被触发时,这个回调函数将被调用。 一个事件应该能够有多个监听器。当触发带有多个回调函数的事件时,应按照订阅的顺序依次调用每个回调函数。应返回一个结果数组。你可以假设传递给 subscribe 的回调函数都不是引用相同的。 subscribe 方法还应返回一个对象,其中包含一个 unsubscribe 方法,使用户可以取消订阅。当调用 unsubscribe 方法时,回调函数应该从订阅列表中删除,并返回 undefined。
emit - 这个方法接收两个参数:一个作为字符串的事件名和一个可选的参数数组,这些参数将传递给回调函数。如果没有订阅给定事件的回调函数,则返回一个空数组。否则,按照它们被订阅的顺序返回所有回调函数调用的结果数组。
知识点:
哈希表
思路
使用哈希表记录一个事件的所有回调函数,对于每个回调函数,我们的用一个数据结构保存它,保存它的函数内容和取消状态。我们对每个函数函数的订阅返回一个取消函数,这个函数就是将这个数据结构的取消状态设置为 true。
当我们执行某个事件时,依次执行哈希表对应的每一个回调函数,如果它的取消状态是 true,那么则跳过它。
代码
class EventEmitter {
m = {};
subscribe(event, cb) {
if (this.m[event] == null) {
this.m[event] = [];
}
let e = {
cb: cb,
unsub: false
}
this.m[event].push(e);
return {
unsubscribe: () => {
e.unsub = true
}
};
}
emit(event, args = []) {
if (this.m[event] == null) {
return [];
}
let re = [];
for (var i = 0; i < this.m[event].length; i++) {
if (this.m[event][i].unsub == false) {
re.push(this.m[event][i].cb(...args))
}
}
return re;
}
}
/**
* const emitter = new EventEmitter();
*
* // Subscribe to the onClick event with onClickCallback
* function onClickCallback() { return 99 }
* const sub = emitter.subscribe('onClick', onClickCallback);
*
* emitter.emit('onClick'); // [99]
* sub.unsubscribe(); // undefined
* emitter.emit('onClick'); // []
*/
2695. 包装数组
题面
创建一个名为 ArrayWrapper 的类,它在其构造函数中接受一个整数数组作为参数。该类应具有以下两个特性:
当使用 + 运算符将两个该类的实例相加时,结果值为两个数组中所有元素的总和。
当在实例上调用 String() 函数时,它将返回一个由逗号分隔的括在方括号中的字符串。例如,[1,2,3] 。
知识点:
模拟
思路
针对题目的两个要求,将 ArrayWrapper 的 valueOf 定义为返回整个数组数值的和,将 toString 定义为返回要求的字符串
代码
/**
* @param {number[]} nums
*/
var ArrayWrapper = function (nums) {
this.nums = nums
};
ArrayWrapper.prototype.valueOf = function () {
let sum = 0;
for (let i = 0; i < this.nums.length; i++) {
sum+=this.nums[i]
}
return sum;
}
ArrayWrapper.prototype.toString = function () {
return "[" + this.nums.toString() + "]"
}
/**
* const obj1 = new ArrayWrapper([1,2]);
* const obj2 = new ArrayWrapper([3,4]);
* obj1 + obj2; // 10
* String(obj1); // "[1,2]"
* String(obj2); // "[3,4]"
*/
2700. 两个对象之间的差异
题面
请你编写一个函数,它接收两个深度嵌套的对象或数组 obj1 和 obj2 ,并返回一个新对象表示它们之间差异。
该函数应该比较这两个对象的属性,并识别任何变化。返回的对象应仅包含从 obj1 到 obj2 的值不同的键。对于每个变化的键,值应表示为一个数组 [obj1 value, obj2 value] 。不存在于一个对象中但存在于另一个对象中的键不应包含在返回的对象中。在比较两个数组时,数组的索引被视为它们的键。最终结果应是一个深度嵌套的对象,其中每个叶子的值都是一个差异数组。
你可以假设这两个对象都是 JSON.parse 的输出结果。
知识点:
深度优先遍历
思路
dfs 递归遍历给出的两个内容:
如果不是对象和数组类型而是普通类型,那么我们比较两个内容的值是不是相等,不同则返回数组包装两个内容。
如果是数组或者数组类型,需要创建一个 object 来保存返回值。先判断类型是否一致,不一致直接返回;否则遍历两个内容,因为数组的索引将作为它的key,所以 Object.keys( ) 函数可以直接获取其键。之后对于每个键,判断是不是在另一个内容中存在,如果存在则递归这两个内容中此键的内容,如果有差异则保存到返回值 object 中,否则继续遍历。
代码
/**
* @param {object} obj1
* @param {object} obj2
* @return {object}
*/
function objDiff(obj1, obj2) {
let diff = {};
if (typeof obj1 === 'object' && typeof obj2 === 'object') {
if(Array.isArray(obj1) != Array.isArray(obj2)){
return [obj1, obj2];
}
for (const key of Object.keys(obj1)) {
if (obj2.hasOwnProperty(key)) {
const result = objDiff(obj1[key], obj2[key])
if (result && Object.keys(result).length) {
diff[key] = result
}
}
}
} else {
if (obj1 !== obj2) {
return [obj1, obj2]
}
}
return diff;
};
2703. 返回传递的参数的长度
题面
请你编写一个函数 argumentsLength,返回传递给该函数的参数数量。
知识点:
无
思路
纯简单题目,通过 args 数组获取所有参数,返回数组的长度即可
代码
/**
* @return {number}
*/
var argumentsLength = function(...args) {
return args.length;
};
/**
* argumentsLength(1, 2, 3); // 3
*/
2704. 相等还是不相等
题面
请你编写一个名为 expect 的函数,用于帮助开发人员测试他们的代码。它应该接受任何值 val 并返回一个包含以下两个函数的对象。
toBe(val) 接受另一个值并在两个值相等( === )时返回 true 。如果它们不相等,则应抛出错误 “Not Equal” 。
notToBe(val) 接受另一个值并在两个值不相等( !== )时返回 true 。如果它们相等,则应抛出错误 “Equal” 。
知识点:
错误处理
思路
返回两个函数,通过 === 比较给出的值和传入的值是不是相等:
toBe 函数相等返回 true ,否则 throw 一个error “Not Equal”
notToBe 函数则不相等返回 true,否则 throw 一个error “Equal”
代码
/**
* @param {string} val
* @return {Object}
*/
var expect = function (val) {
return {
toBe: (t) => {
if (t === val) {
return true
} else {
throw "Not Equal"
}
},
notToBe: (t) => {
if (t !== val) {
return true
} else {
throw "Equal"
}
},
}
};
/**
* expect(5).toBe(5); // true
* expect(5).notToBe(5); // throws "Equal"
*/