💪 🚀 持续更新学习或工作中遇到的问题。
折叠部分查看答案,祝好运 ❤️ 😄!
答案
map方法
map()
方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
var new_array = arr.map(function callback(currentValue[,index[, array]]) {
// Return element for new_array
}[, thisArg])
callback
回调函数需要三个参数, 我们通常只使用第一个参数 (其他两个参数是可选的)。
currentValue
是callback 数组中正在处理的当前元素。
index
可选, 是callback 数组中正在处理的当前元素的索引。
array
可选, 是callback map 方法被调用的数组。
另外还有thisArg
可选, 执行 callback 函数时使用的this 值
const arr = [1, 2, 3];
arr.map((num) => num + 1); // [2, 3, 4]
parseInt
parseInt()
是JavaScript的内置函数,解析一个字符串参数,并返回一个指定基数的整数 (数学系统的基础)。
const intValue = parseInt(string[, radix]);
string
要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用 ToString 抽象操作)。字符串开头的空白符将会被忽略。
radix
一个介于包含2和36之间的整数(数学系统的基础),表示上述字符串的基数。基数未填或者传0那么将默认为10。
返回值
返回一个整数或NaN
运行流程
['1', '2', '3'].map((item, index) => parseInt(item, index))
// 每次执行
parseInt('1', 0) // 1
parseInt('2', 1) // NaN
parseInt('3', 2) // NaN
如何达到预期效果
['1', '2', '3'].map((item) => parseInt(item))
OR
['1', '2', '3'].map(Number)
parseInt(value,radix)转换规则(伴随radix)总结
- value若不是字符串,先转为字符串
- 检查radix是否在区间[2,36]之间,0或不填默认为10,不再此区间直接返回NaN,parseInt('2', 1) 1不在有效值内,返回NaN
- 依顺序找到字符串value中满足小于radix的数,直到无效数值型,parseInt('3', 2) 2表示为按照二进制解析,只有0 1属于有效数字,3不为有效数字,返回NaN
let i,j,k;
for( i = 0 , j = 0; i < 10 , j < 6; i++, j++ ) {
k = i+j;
}
console.log('k:', k)
答案
解析:考察for 循环以及逗号操作。
逗号操作符 逗号操作符 对它的每个操作对象求值(从左至右),然后返回最后一个操作对象的值。
console.log((1,2)) // 2
console.log((x=2, ++x)) // 3
const a = {
foo: function() {
console.log(this === window);
}
};
a.foo(); // this此时指向a 显然this !== window
// 使用逗号操作符
(0, a.foo)(); // 'true' in console
// 使用逗号操作符之后等价于
const b = a.foo
b() // 'true' in console
回到题目 for循环中,循环条件判断时,由于逗号操作符的缘故,继续循环的条件为最后一个,流程如下: i=0, j=0 => 0 i=1, j=1 => 2 i=2, j=2 => 4 i=3, j=3 => 6 i=4, j=4 => 8 i=5, j=5 => 10
题目修改
let i,j,k;
for( i = 0 , j = 0; i < 6 , j < 10; i++ , j++ ) {
k = i+j;
}
console.log('k:', k) // k: 18
总结 如果j的最终值大于i,同样会以j的作循环条件遍历次数。
const obj = {
'2': 3,
'3': 4,
'length': 2,
'splice': Array.prototype.splice,
'push': Array.prototype.push
}
console.log(obj)
obj.push(1)
obj.push(2)
console.log(obj)
答案
// 考察点
// 1. 当对象存在splice属性时 控制台输出会是数组的形式 但obj仍然是对象类型
// 2. push的位置取决于此时length的长度 此时length为2 所以执行push会从小标2开始 2次push覆盖了3、4,length长度变为4
// 存在splice函数
const o = {
length: 1,
splice: Array.prototype.splice,
push: Array.prototype.push
}
o.push(1)
o.push(2)
console.log('o:', o) // [empty, 1, 2, splice: f, push: f]
// 不存在splice函数时当对象处理
const o2 = {
length: 1,
push: Array.prototype.push
}
o.push(1)
o.push(2)
console.log('o2:', o2) // {length: 1, push: ƒ}
// example 1
var a={}, b='123', c=123;
a[b]='b';
a[c]='c';
console.log(a[b]);
// example 2
var a={}, b=Symbol('123'), c=Symbol('123');
a[b]='b';
a[c]='c';
console.log(a[b]);
// example 3
var a={}, b={key:'123'}, c={key:'456'};
a[b]='b';
a[c]='c';
console.log(a[b]);
答案
解析:此题考察对象的键名的转换
-
对象的键名只能是字符串和 Symbol 类型
-
其他类型的键名会被转换成字符串类型
-
对象转字符串默认会调用 toString 方法
// example 1 var a={}, b='123', c=123; a[b]='b'; // c 的键名会被转换成字符串'123',这里会把 b 覆盖掉。 a[c]='c'; // 输出 c console.log(a[b]);
// example 2 var a={}, b=Symbol('123'), c=Symbol('123'); // b 是 Symbol 类型,不需要转换。 a[b]='b'; // c 是 Symbol 类型,不需要转换。任何一个 Symbol 类型的值都是不相等的,所以不会覆盖掉 b。 a[c]='c'; // 输出 b console.log(a[b])
// example 3 var a={}, b={key:'123'}, c={key:'456'}; // b 不是字符串也不是 Symbol 类型,需要转换成字符串。 // 对象类型会调用 toString 方法转换成字符串 [object Object]。 a[b]='b'; // c 不是字符串也不是 Symbol 类型,需要转换成字符串。 // 对象类型会调用 toString 方法转换成字符串 [object Object]。这里会把 b 覆盖掉。 a[c]='c'; // 输出 c console.log(a[b]);
add(1); // 1
add(1)(2); // 3
add(1)(2)(3);// 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3); // 6
答案
考察:函数柯里化
curry 的概念:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
// 参数固定的情况
const fn = (a, b, c) => a + b + c
const curry = (fn) => {
return function curried(...args) {
if (args.length < fn.length) {
return function () {
return curried(...args.concat(Array.from(arguments)))
}
}
return fn(...args)
}
}
const add = curry(fn)
console.log(add(1, 2)(4))
// 2.确定参数的情况
function add() {
const colls = [...arguments]
const fn = function () {
const fn_args = [...arguments]
colls.push(...fn_args)
return fn
}
fn.toString = function () {
return colls.reduce((a, b) => a + b)
}
return fn
}
function Foo(){
getName = function(){
console.log(1);
};
return this;
}
Foo.getName = function(){
console.log(2);
}
Foo.prototype.getName = function(){
console.log(3);
}
var getName = function(){
console.log(4);
}
function getName(){
console.log(5);
}
// Output?
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
答案
分析:考查原型原型链、隐式定义、变量提升等知识点。
Foo.getName()
代码中直接定义,输出2
getName()
var 变量定义引起的变量提升,理解以下代码
var getName = function(){
console.log(4);
}
function getName(){
console.log(5);
}
// 等价于
var getName
function getName() {
console.log(5)
}
getName = function(){
console.log(4);
}
显然 getName
已被覆盖,所以输出4
Foo().getName()
调用Foo
方法,函数里面隐式定义了一个getName
方法,返回this实例,考虑浏览器环境运行,此时相当于Window.Foo().getName()
, this指向window,而隐式定义的变量则相当于window的属性,此时getName
也是覆盖了输出为4的getName
, 即此时``Foo().getName()`输出1
getName()
等价于上面的一个输出 1
new Foo.getName()
即 new (Foo.getName()) 输出2
new Foo().getName()
即(new Foo()).getName(), 获取原型上的getName方法 输出3
new new Foo().getName()
即类似于上面2 输出3
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // ?
this.setState({val: this.state.val + 1});
console.log(this.state.val); // ?
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // ?
this.setState({val: this.state.val + 1});
console.log(this.state.val); // ?
}, 0);
}
render() {
return null;
}
};
答案
解析:考察react中setState的原理。
- 合成事件及生命周期函数setState表现为异步行为,setState将会批处理
- 原生事件或setTimeout等异步任务中表现为同步行为
function foo() {
let x = (y = 0);
x++;
y++;
return x;
}
console.log(foo(), typeof x, typeof y);
答案
解析:
- 首先foo()的调用,x++之后输1
- foo函数x,y的定义等价于let x; window.y = 0; x = window.y,由于函数作用域的缘故,全局上下文将不能访问到x,所以typeof x 为undefined
- y全局,显然为number
var y = 1;
if (function f() {}) {
y += typeof f;
}
console.log(y);
答案
解析:
- function f(){} 对象类型,总是返回true
- 由于function f(){}是在运行时调用,没有声明的地方,所以是undefined,结果显而易见。
function printNumbers(first, second, first) {
console.log(first, second, first);
}
printNumbers(1, 2, 3);
console.log(String.prototype.trimLeft.name === "trimLeft");
console.log(String.prototype.trimLeft.name === "trimStart");
答案
解析:由于 web 兼容性的原因,旧的方法名称 'trimLeft' 仍然充当 'trimStart' 的别名。因此,“trimLeft”的原型始终是“trimStart
console.log(Math.max());
console.log(10 == [10]);
console.log(10 == [[[[[[[10]]]]]]]);
let numbers = [1, 2, 3, 4, NaN];
console.log(numbers.indexOf(NaN));
答案
解析:indexOf 在内部使用严格相等运算符 (===) 并且 NaN === NaN 评估为假。由于 indexOf 无法在数组中找到 NaN,因此它总是返回 -1。但是可以使用其他方式来判断
let numbers = [1, 2, 3, 4, NaN];
console.log(numbers.findIndex(Number.isNaN)); // 4
console.log(numbers.includes(NaN)); // true
var set = new Set();
set.add("+0").add("-0").add(NaN).add(undefined).add(NaN);
console.log(set);
console.log(
JSON.stringify({ myArray: ["one", undefined, function () {}, Symbol("")] })
);
console.log(
JSON.stringify({ [Symbol.for("one")]: "one" }, [Symbol.for("one")])
);
答案
解析:
- undefined、Functions 和 Symbols 不是有效的 JSON 值。因此,这些值要么被省略(在对象中),要么被更改为 null(在数组中)。因此,它返回值数组的空值。
- 所有符号键属性将被完全忽略。因此它返回一个空对象({})。
const { a: x = 10, b: y = 20 } = { a: 30 };
console.log(x);
console.log(y);
const obj = { key: "value" };
const array = [...obj];
console.log(array);
答案
解析:
- 扩展语法只能应用于可迭代对象
- 默认情况下,对象是不可迭代的,但是当在数组中使用时,或者与 map()、reduce() 和 assign() 等迭代函数一起使用时,它们变得可迭代。如果你仍然尝试这样做,它仍然会抛出 TypeError: obj is not iterable.
const myGenerator = (function* () {
yield 1;
yield 2;
yield 3;
})();
for (const value of myGenerator) {
console.log(value);
break;
}
for (const value of myGenerator) {
console.log(value);
}
const squareObj = new Square(10);
console.log(squareObj.area);
class Square {
constructor(length) {
this.length = length;
}
get area() {
return this.length * this.length;
}
set area(value) {
this.area = value;
}
}
let msg = "Good morning!!";
msg.name = "John";
console.log(msg.name);
let person = { name: 'Lydia' };
const members = [person];
person = null;
console.log(members);
答案
解析:变量person指向对象,对象在设置彼此相等时通过引用进行交互,当您将一个变量的引用分配给另一个变量时,您制作了该引用的副本。当person重新赋值为null,不会影响之前的数组的引用。
function greeting() {
throw 'Hello world!';
}
function sayHi() {
try {
const data = greeting();
console.log('It worked!', data);
} catch (e) {
console.log('Oh no an error:', e);
}
}
sayHi();
// counter.js
let counter = 10;
export default counter;
// index.js
import myCounter from './counter';
myCounter += 1;
console.log(myCounter);
const name = 'Lydia';
age = 21;
console.log(delete name);
console.log(delete age);
答案
解析:删除操作符返回一个布尔值:成功删除时为 true,否则返回 false。但是,使用 var、const 或 let 关键字声明的变量不能使用 delete 运算符删除。 name由const声明,返回false,但age则是添加到window对象上,作为window对象的一个属性,能删除成功。
const settings = {
username: 'lydiahallie',
level: 19,
health: 90,
};
const data = JSON.stringify(settings, ['level', 'health']);
console.log(data);
答案
解析:
- JSON.stringify 的第二个参数是替换器。替换器可以是一个函数或一个数组,并允许您控制应该对值进行字符串化的内容和方式。
- 如果替换器是一个数组,那么只有包含在数组中的属性名称会被添加到 JSON 字符串中。
- 如果替换器是一个函数,则在您要字符串化的对象中的每个属性上都会调用此函数。此函数返回的值将是属性添加到 JSON 字符串时的值。
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
答案
解析:
- 使用 import 关键字,所有导入的模块都被预先解析。
- 这是 CommonJS 中的 require() 和 import 的区别!使用 require(),你可以在代码运行时按需加载依赖。
- 使用require,你能得到你以为的答案。
function foo() {
return 'Here is pizza!';
}
const bar = () =>
"Here's chocolate... now go hit the gym already.";
console.log(foo.prototype);
console.log(bar.prototype);
答案
解析:
- 普通函数如foo,都存在prototype属性,它是一个拥有constructor属性的一个对象
- 箭头函数bar,不存在prototype属性的,访问将会得到undefined
const info = {
[Symbol('a')]: 'b',
};
console.log(info);
console.log(Object.keys(info));
答案
解析:考察symbol作为键值
- symbol作为键值是不可枚举的,Object.keys将返回[]
- 打印整个对象时,所有的属性都是可见的,即使是不可枚举的属性
const getList = ([x, ...y]) => [x, y]
const getUser = user => { name: user.name, age: user.age }
const list = [1, 2, 3, 4]
const user = { name: "Lydia", age: 21 }
console.log(getList(list))
console.log(getUser(user))
答案
解析:考察展开运算符与箭头函数中返回对象
- 展开运算符中,对应x=1,y=[2,3,4],所有返回[1,[2,3,4]]
- 而箭头函数中返回一个对象时,只有一条语句返回,则需要使用括号进行包裹。 即const getUser = user => ({ name: user.name, age: user.age })
const name = 'Lydia';
console.log(name());
答案
解析:TypeError
- 变量名保存一个字符串的值,它不是一个函数,因此不能调用
- 当值不是预期的类型时,会抛出 TypeErrors。 JavaScript 期望 name 是一个函数,因为我们试图调用它
- 当你编写了无效的 JavaScrip 内容时,会抛出 SyntaxErrors
- 当 JavaScript 无法找到对您尝试访问的值的引用时,会引发 ReferenceErrors
const colorConfig = {
red: true,
blue: false,
green: true,
black: true,
yellow: false,
};
const colors = ['pink', 'red', 'blue'];
console.log(colorConfig.colors[1]);
JSON.parse();
答案
JSON string to a JavaScript value:
const jsonNumber = JSON.stringify(4); // '4'
JSON.parse(jsonNumber); // 4
const jsonArray = JSON.stringify([1, 2, 3]); // '[1, 2, 3]'
JSON.parse(jsonArray); // [1, 2, 3]
const jsonArray = JSON.stringify({ name: 'Lydia' }); // '{"name":"Lydia"}'
JSON.parse(jsonArray); // { name: 'Lydia' }
let name = 'Lydia';
function getName() {
console.log(name);
let name = 'Sarah';
}
getName();
const name = 'Lydia Hallie';
const age = 21;
console.log(Number.isNaN(name));
console.log(Number.isNaN(age));
console.log(isNaN(name));
console.log(isNaN(age));
const randomValue = 21;
function getInfo() {
console.log(typeof randomValue);
const randomValue = 'Lydia Hallie';
}
getInfo();
const person = { name: 'Lydia Hallie' };
Object.seal(person);
person.name = "Evan Bacon"
console.log(person)
let randomValue = { name: "Lydia" }
randomValue = 23
if (!typeof randomValue === "string") {
console.log("It's not a string!")
} else {
console.log("Yay it's a string!")
}
答案
- !typeof randomValue === "string" 会先计算typeof randomValue 为'number' string,取非之后为false,与string比较总是返回false,所以走else语句
var a = ?
if (a == 1 && a == 2 && a == 3) {
console.log('xxx')
}
答案
解析:考察js中的隐式类型转换。
引用类型转换原始值的步骤
- 先调用对象的Symbol.toPrimitive方法,如果不存在这个
- 再调用valueOf方法 如果该方法返回一个原始值那么就用此原始值
- 否则,调用toString方法,返回字符串表示
答案1
const a = {
value: 1,
valueOf() {
return this.value++
},
}
答案2
const a = [1,2,3]
a.toString = a.shift
答案
const x = Number.MAX_SAFE_INTEGER + 1
console.log(x === x + 1) // true
function foo(a, b = 1, c) {
console.log(foo.length)
}
foo(1,2,3)
答案
解析:函数长度属性及存在默认参数
- es6引入了默认参数。此前,函数length属性用于返回所有函数参数编号
- 默认参数的引入,length属性发生了变化,带默认值的参数是可选的,所以这样的参数不计入length中
- 默认参数后面的参数也是可选,后面的也不会计入长度属性计算中
const obj = {};
Object.defineProperty(obj, 'count', {
value: '100'
});
console.log(obj.count);
delete obj.count;
console.log(obj.count);
答案
解析:Object.defineProperty 方法及其默认参数
- 语法格式:Object.defineProperty(obj, prop, descriptors)
- descriptors属性描述符
- 数据描述符 (值 可写 可枚举 可配置)默认情况,不可写 不可枚举 不可配置 可配置属性指定是否可以从对象中删除属性,以及将来是否可以更改属性描述符。题目中由于默认为false 将会别忽略,严格模式下 将会报错
- 访问描述符 (get set方法)
function Foo() {
this.flag = true;
}
const foo = new Foo();
const bar = Object.create(foo);
const clone1 = { ...bar };
const clone2 = Object.assign({}, bar);
console.log(bar.flag, clone1.flag, clone2.flag);
答案
解析:Object.create 和 Object.assign 克隆对象
- 语法:Object.create(proto[,propertiesObject]) 创建一个新对象 并以proto作为原型 propertiesObject对新创建的对象进行初始化
- admin本身是没有flag属性 访问时找到其原型对象上的属性
- 通过展开符与Object.assign()克隆会忽略其原型
他们原型为
admin.__proto__ Foo { flag: true },
clone1.__proto__ [Object: null prototype] {},
clone2.__proto__ [Object: null prototype] {}
克隆包含其原型
const clone1 = { __proto__: Object.getPrototypeOf(obj), ...obj };
const clone2 = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
const str = 'string';
const str2 = String('string');
console.log(str instanceof String);
console.log(str2 instanceof String);
答案
解析:字符串函数和 instanceof 运算符
- instanceof 运算符仅适用于对象
- 字符串文字'string'是原始的
- 非构造函数上下文中的字符串调用(不使用 new 关键字调用)返回一个原始字符串
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a.x);
console.log(b.x);