★此文章包含精挑出来的踩坑题目,在业余时间还会不断精选,目标就是捉JS虫
”
1 对象键自动转换为字符串
const a = {};
const b = { key: "b" };
const c = { key: "c" };
a[b] = 123;
a[c] = 456;
console.log(a[b]);
> 456
【解释】 当对象自动转换为字符串化时,它变成了[Object object]。 所以我们在这里说的是a["Object object"] = 123。 然后,我们可以尝试再次做同样的事情。 c对象同样会发生隐式类型转换。那么,a["Object object"] = 456。 然后,我们打印a[b],它实际上是a["Object object"]。 我们将其设置为456,因此返回456。
2 结构函数是否使用new
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
const person1 = new Person('h', 'hy')
const person2 = Person('h', 'hy')
console.log(person1)
console.log(person2)
> Person {firstName: "h", lastName: "hy"}
> undefined
【解释】person1使用new,指向我们创建的空对象;person2没有使用new,this指向「全局对象」
3 给构造函数添加属性和原型添加属性
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const member = new Person("Lydia", "Hallie");
Person.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
console.log(member.getFullName());
> TypeError
【解释】不能给构造函数直接添加属性,因为可以这样添加的话,将浪费大量内存空间,因为每个实例仍然具有该属性,这将占用每个实例的内存空间。
可以写在原型上
Person.prototype.getFullName = function () {
return `${this.firstName} ${this.lastName}`;
}
4 除了【基本对象】base object),所有对象都有原型
console.log(Math.prototype);
> undefined
【解释】 JS基本对象:Function, Array, Boolean, Date, Math, Number, String, RegExp, Global
5 + 隐式类型转换
console.log(+'2'+1);
console.log(2+'1');
console.log('2'+1);
> 3
> "21"
> "21"
【解释】+两边都有数字,但任何一边有字符串会进行类型转换
6 函数参数用标记模板字面量
function getPersonInfo(one, two, three) {
console.log(one)
console.log(two)
console.log(three)
}
const person = 'Lydia'
const age = 21
getPersonInfo`${person} is ${age} years old`
> Array ["", " is ", " years old"]
> "Lydia"
> 21
getPersonInfo(person,age)
> "Lydia"
> 21
> undefined
【解释】如果使用标记模板字面量,第一个参数的值总是包含字符串的数组。其余的参数获取的是传递的表达式的值!
7 对象引用比较
function checkAge(data) {
if (data === { age: 18 }) {
console.log('You are an adult!')
} else if (data == { age: 18 }) {
console.log('You are still an adult.')
} else {
console.log(`Hmm.. You don't have an age I guess`)
}
}
checkAge({ age: 18 })
> Hmm.. You don't have an age I guess
【解释】在测试相等性时,基本类型通过它们的值(value)进行比较,而对象通过它们的引用进行比较。
题中我们正在比较的两个对象不是同一个引用:作为参数传递的对象引用的内存位置,与用于判断相等的对象所引用的内存位置并不同。
这也是 { age: 18 } === { age: 18 } 和 { age: 18 } == { age: 18 } 都返回 false 的原因。
8 扩展运算符作为参数,并且只能作为最后一个参数
function getAge(...args) {
console.log(typeof args)
console.log( args)
}
getAge(21)
> "object"
> Array [21]
function getAge(a, ...args, b) {
return a
}
> 报错
【解释】扩展运算符(...args)会返回实参组成的数组。而数组是对象,因此 typeof args 返回 "object"
...args只能作为最后一个参数
9 箭头函数返回对象,必须在圆括号之间编写它
const getUser = user => { name: user.name, age: user.age }
const user = { name: "Lydia", age: 21 }
console.log(getUser(user))
> undefined
【解释】对于箭头函数,如果只返回一个值,我们不必编写花括号。但是,如果您想从一个箭头函数返回一个对象,必须在圆括号之间编写它,否则不会返回任何值!下面的函数将返回一个对象:
const getUser = user => ({ name: user.name, age: user.age })
10 Symbol类型
const info = {
[Symbol('a')]: 'b'
}
console.log(info)
console.log(Object.keys(info))
> {Symbol('a'): 'b'}
> []
【解释】Object.keys方法返回对象上的所有可枚举的键属性。Symbol类型是不可见的,并返回一个空数组。 记录整个对象时,所有属性都是可见的,甚至是不可枚举的属性。
Symbol类型是不可枚举的;
Symbol代表完全唯一的值,防止对象意外名称冲突,例如当使用2个想要向同一对象添加属性的库时;
可以使用Object.getOwnPropertySymbols()方法访问 Symbol;
11 可以将类设置为等于其他类/函数构造函数
class Person {
constructor() {
this.name = "hhy"
this.age = 18
}
}
Person = class AnotherPerson {
constructor() {
this.name = "gh"
}
}
const member = new Person()
console.log(member.name)
console.log(member.age)
> "gh"
> undefined
【解释】将类设置为等于其他类/函数构造函数,实例改变
12 JS引擎自动添加分号
function nums(a, b) {
if
(a > b)
console.log('a is bigger')
else
console.log('b is bigger')
return
a + b
}
console.log(nums(4, 2))
console.log(nums(1, 2))
> a is bigger, undefined
> b is bigger, undefined
【解释】return后面自动添加分号,所以代码规范最好带上分号
13 push返回数组长度
let newList = [1, 2, 3].push(4)
console.log(newList.push(5))
> Error
【解释】push方法返回数组的长度,而不是数组本身
14 this指向:函数内包含函数,this还是指向window
var obj1 = {
foo1: function () {
console.log(this) //obj1对象
function inner () {
console.log(this) //window
console.log(this.a) //3
}
inner() //函数内包含函数,this还是指向window
}
}
var a = 3
obj1.foo1()
> obj1对象
> window
> 3
【解释】函数内包含函数,this还是指向window
15 变量提升
var name = 'World!';
(function () {
if (typeof name === 'undefined') {
var name = 'hhy'; //变量提升
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
> "Goodbye hhy"
var str = 'World!';
(function (name) {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})(str);
> Hello World
【解释】
第一题name变量提升了所以typeof name === 'undefined'成立;
第二题因为name已经变成函数内局部变量
16 const声明的变量,不能修改它的指针指向,但是可以改变其存储值
const obj = {
name: '小辉辉'
};
obj = [10, 20];
obj.name = '辉太郎';
console.log(obj);
> Uncaught TypeError: Assignment to constant variable.
> {name: '辉太郎'}
【解释】
报错的原因是:obj这个变量不能在和其它值进行关联了,也就是不能修改const声明变量的指向。
后面值对是因为可以在不改变指向的情况下,修改堆内存中的信息(这样也把值更改了);
所以记住:const声明的变量,不能修改它的指针指向,但是可以改变其存储值的
17 JS会认为大括号{}开头的是一个空的代码块
console.log({}+[])
> "[object Object]"
{}+[]
> 0 控制台直接打印,不用console.log
【解释】
第一题+转换为字符串;
第二题{}被忽略了,直接执行了+[],结果为0。
18 作用域
var a = 0,
b = 0;
function A(a) {
A = function (b) {
console.log('inner:',a + b++)
}
console.log('outer:',a++)
}
A(1)
A(2) //执行的全局A,也就是A里面的A,a已经变成2,b是2,2+2等于4
> "outer:" 1
> "inner:" 4
仅仅执行
A(2)
> "outer:" 2
【解释】
第一题A(2)执行的是A函数里面的A,外面的A先压入栈,里面的再压入A,所以第二次执行的是里面的 ;
第一题A(2)执行的是外面函数
19 JSON.stringify
JSON.stringify('ni')
> ""ni""
【解释】字符串再包字符串
20 forEach 和 map 区别
var array = [1,2,3,4,5];
var x = array.forEach(function(value,index){
console.log(value); //1 2 3 4 5 可遍历到所有数组元素
return value + 10
});
console.log(x); //undefined 无论怎样,总返回undefined
var y = array.map(function(value,index){
console.log(value); //1 2 3 4 5 可遍历到所有数组元素
return value + 10
});
console.log(y); //[11, 12, 13, 14, 15]
【解释】
forEach() 方法对数组的每个元素执行一次提供的函数。总是返回undefined;
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。返回值是一个新的数组;