手写算法: 将有序数组转换为二叉搜索树
思路
一个有序数组对于 BST 来说就是一个中序遍历的结果,跟节点在数组的中心,数组左侧是左子树元素,右侧是右子树元素。
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {number[]} nums
* @return {TreeNode}
*/
var sortedArrayToBST = function(nums) {
const buildTree = (nums, left, right) => {
if (left > right) return null;
const mid = left + ((right - left) >> 1);
const root = new TreeNode(nums[mid]);
root.left = buildTree(nums, left, mid - 1);
root.right = buildTree(nums, mid + 1, right);
return root;
};
return buildTree(nums, 0, nums.length - 1);
};
手写题: 压缩字符串
理解题意:这里是找连续的字符串,不是字符出现的次数
/**
* @param {string} str
* @return {string}
*/
function compress(str) {
let result = [];
let count = 0;
for (let i = 0; i < str.length; i++) {
count++;
if (str[i] !== str[i + 1]) {
result.push(str[i]);
count > 1 && result.push(count);
count = 0;
}
}
return result.join('');
}
JS 简答题
method
// case 1
const obj1 = {
foo() {
console.log(super.foo());
},
};
Object.setPrototypeOf(obj1, {
foo() {
return 'bar';
},
});
obj1.foo();
// case 2
const obj2 = {
foo: function() {
console.log(super.foo());
},
};
Object.setPrototypeOf(obj2, {
foo() {
return 'bar';
},
});
obj2.foo();
解析
obj1 是Object的普通实例,它没有prototype属性,只有构造函数才具有prototype属性。实例只能拥有__proto__属性,指向由其构造函数的prototype。
而,obj2 定义一个名为 foo 的属性,该属性是一个函数。【这是一个普通函数】
方法和普通函数之前的区别:
方法有个
HomeObject(同源对象),这就是允许他们使用 super 的原因方法没有构造器,不能被 new
在方法的作用域中,不能用方法名做绑定(与命名的函数表达式不同)
try...catch
var a = 'a';
try {
throw new Error('BFE.dev');
} catch {
var a = 'a1';
}
console.log(a);
var b = 'b';
try {
throw new Error('BFE.dev');
} catch (b) {
var b = 'b1';
}
console.log(b);
var c = 'c';
try {
throw new Error('BFE.dev');
} catch (error) {
var c = 'c1';
}
console.log(c);
解析
try...catch语句,两块作用域,在 try 中跑出异常,会走到 catch 中。catch 传进来的值只能在 catch 作用域内使用。
// 全局变量a
var a = 'a';
try {
throw new Error('BFE.dev');
} catch {
// 覆盖全局变量a
var a = 'a1';
}
console.log(a); // a1
// 全局变量b
var b = 'b';
try {
throw new Error('BFE.dev');
} catch (b) {
// 局部变量b,其实相当于定义多一个变量 var _b = 'b1',没有对全局变量b进行操作
var b = 'b1';
}
console.log(b);
var c = 'c';
try {
throw new Error('BFE.dev');
} catch (error) {
var c = 'c1';
}
console.log(c); // 同a
reference type
const obj = {
msg: 'BFE',
foo() {
console.log(this.msg);
},
bar() {
console.log('dev');
},
};
obj.foo();
obj.foo();
(obj.foo || obj.bar)();
解析
obj.foo()与(obj.foo)()都是一个意思,作用域都是指向 obj(obj.foo || obj.bar)(),()是一个分组运算符,先执行括号里面的。||表达式返回第一个真实的值,那么这里返回的是 obj.foo。 拿到的是匿名函数,作用域指向 window
Promise then callbacks II
Promise.resolve(1)
.then(val => {
console.log(val);
return val + 1;
})
.then(val => {
console.log(val);
})
.then(val => {
console.log(val);
return Promise.resolve(3).then(val => {
console.log(val);
});
})
.then(val => {
console.log(val);
return Promise.reject(4);
})
.catch(val => {
console.log(val);
})
.finally(val => {
console.log(val);
return 10;
})
.then(val => {
console.log(val);
});
解析
return 了值,接下来的 then 和 catch 才能接收到
Promise.reject(),catch 和 finally 才能拿到,then 拿不到
finally 的回调函数不接收任何参数,它仅用于无论最终结果如何都要执行的情况。不会对 Promise 产生影响,即不会影响后续的函数结果。
Promise.resolve(1)
.then(val => {
console.log(val); // 1
return val + 1; // return 2
})
.then(val => {
console.log(val); // 2
})
.then(val => {
console.log(val); // undefined, 上一层没有传东西进来
return Promise.resolve(3).then(val => {
// return一个Promise
console.log(val); // 3
});
})
.then(val => {
// 接受的是上一个Promise return出来的值,但是上一个Promise没有return东西出来
console.log(val); // undefined
return Promise.reject(4); // 执行Promise.reject(),catch能拿到,then拿不到
})
.catch(val => {
console.log(val); // 4
})
.finally(val => {
// 回调函数不接收入参
console.log(val); // undefined
return 10; // 不会对Promise产生影响
})
.then(val => {
console.log(val); // undefined,拿不到finally return出来的,因为它finally不会对promise产生影响,传不进来
});
zero
console.log(1 / 0);
console.log(-1 / 0);
console.log(0 / 0);
console.log(0 === -0);
console.log(Object.is(0, -0));
console.log(Object.is(0, Math.round(-0.5)));
console.log(Object.is(0, Math.round(0.5)));
console.log(0 * Infinity);
console.log(Infinity / Infinity);
console.log(Object.is(0, Math.sign(0)));
console.log(Object.is(0, Math.sign(-0)));
console.log(1 / -0);
console.log(1 / 0);
console.log(1n / 0n);
解析
A / B, 被除数 / 除数,0 不能当除数,即 0 不能作为分母。任何数值除以 0,在数学中都会得到错误,这边 js 对以下做了两种情况:0 / 0, 会返回 NaN,这样不会影响程序的执行。非零有限值 / 0,返回 Infinity,至于有没有符号看被除数与除数。
Object.is()与===不相同,差别是它们对待有符号的零和 NaN 不同。例如,=== 运算符(也包括 == 运算符)将数字 -0 和 +0 视为相等,而将Number.NaN与NaN视为不相等。is 则与它们相反。Math.round()四舍五入,-0.5 向上进变成-0, 0.5 向上变成 1ES 规定看大宝典:
非零有限值 / 0,返回 Infinity0 * Infinity与Infinity / Infinity, 返回 NaN
【🔍 ==> 11.5.1 Applying the * Operator】
- Multiplication of an infinity by a zero results in NaN.(无穷大乘以零会得到 NaN)
【🔍 ==> 11.5.2 Applying the / Operator】
- Division of an infinity by an infinity results in NaN. (无穷大除以无穷大导致 NaN)
- Division of a zero by a zero results in NaN; division of zero by any other finite value results in zero, with the sign determined by the rule already stated above. (零除以零导致 NaN; 零除以任何其他有限值导致零,其符号由上述规则确定)
- Division of a non-zero finite value by a zero results in a signed infinity. The sign is determined by the rule already stated above.(将非零有限值除以零,得到有符号无穷大)
Math.sign(),返回一个数字的符号,有 5 种返回值,分别是 1, -1, 0, -0, NaN. 代表的各是正数,负数,正零,负零,NaN。BigInt类型不能除以 0