题目来源于 https://zhuanlan.zhihu.com/p/75359676 ,觉得不错。一边做一边记录。
————CafuChino
1. 下面代码的输出是什么?
function sayHi() {
console.log(name);
console.log(age);
var name = "Lydia";
let age = 21;
}
这道题的考点无非就是考察let的特性和var的变量提升,因为众所周知var存在变量提升。考虑不周的话可能会认为第一行会输出“Lydia”,我第一反应的时候就犯了这个错误。实则不然,即使存在变量提升,调用的时候依然没有为name赋值,所以应该返回 undefined
,而age因为let声明导致死区,变量不提升,会报错 ReferenceError
。
2. 下面代码的输出是什么?
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1);
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1);
}
这道题的考点是ES6中let的块级作用域和js的事件执行机制。答案是333和012
由于JavaScript
中的事件执行机制,setTimeout
函数真正被执行时,循环已经走完。 由于第一个循环中的变量i
是使用var
关键字声明的,因此该值是全局的。 在循环期间,我们每次使用一元运算符++
都会将i
的值增加1
。 因此在第一个例子中,当调用setTimeout
函数时,i
已经被赋值为3
。
在第二个循环中,使用let
关键字声明变量i
:使用let
(和const
)关键字声明的变量是具有块作用域的(块是{}
之间的任何东西)。 在每次迭代期间,i
将被创建为一个新值,并且每个值都会存在于循环内的块级作用域。
3. 下面代码的输出是什么?
const shape = {
radius: 10,
diameter() {
return this.radius * 2;
},
perimeter: () => 2 * Math.PI * this.radius
};
shape.diameter();
shape.perimeter();
这道题的考点是箭头函数的this指代问题。与普通函数不同,箭头函数的this指代 它所在上下文(定义时的位置)的环境 。也就是说实际使用的时候,perimeter中的this指的不是shape,而是window(全局定义的变量和函数归属于window),所以会返回undefined。这里答案是20和NaN(NOT a NUMBER)
4. 下面代码的输出是什么?
+true;
!"Lydia";
答案是1和false,涉及到两个特性:
- 一元加号会把
Boolean
转换成数字,true为1,false为0。 - 字符串是真值,!为非,非真为false。
5. 哪个选项是不正确的?
const bird = {
size: "small"
};
const mouse = {
name: "Mickey",
small: true
};
- A:
mouse.bird.size
- B:
mouse[bird.size]
- C:
mouse[bird["size"]]
- D: All of them are valid
选A(仔细分析过后居然选对了),开发经验!
这道题其实考察的是JavaScript的对象键值,对象键值本质上依然是字符串,方括号里的值会被有限计算。了解这个规律之后其实很好解。A选项mouse对象没有bird键值,会返回undefined,undefined.size自然会报错 Cannot read property "size" of undefined
剩下两个方括号里的值都可以计算出mouse对象有的键值。
6. 下面代码的输出是什么?
let c = { greeting: "Hey!" };
let d;
d = c;
c.greeting = "Hello";
console.log(d.greeting);
答案是"Hello",这道题真的是个大坑,不过有开发经验的话应该会有所体会。如果单纯按照赋值思想考虑的很容易得出,d被赋值的时候依然是{ greeting: "Hey!" }
。所以答案是"Hey"(这是错误的!)原文作者已经写得很好了,所以直接贴上引用:
在
JavaScript
中,当设置它们彼此相等时,所有对象都通过引用进行交互。首先,变量c
为对象保存一个值。 之后,我们将d
指定为c
与对象相同的引用。** 更改一个对象时,可以更改所有对象。 **————引自原文
7. 下面代码的输出是什么?
let a = 3;
let b = new Number(3);
let c = 3;
console.log(a == b);
console.log(a === b);
console.log(b === c);
答案是true,false,false,难度不大,就是==
只比较值,===
不仅比较值还比较类型,对象和整数肯定不会相等。
8. 下面代码的输出是什么?
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor;
}
constructor({ newColor = "green" } = {}) {
this.newColor = newColor;
}
}
const freddie = new Chameleon({ newColor: "purple" });
freddie.colorChange("orange");
此题考点为static,static声明的方法为静态方法,只在构造函数中存在,在子对象中不存在。freddie对象中实际没有colorChange
方法,会报错TypeError
。
9. 下面代码的输出是什么?
let greeting;
greetign = {}; // Typo!
console.log(greetign);
(送分题?)变量不经声明直接使用归属于global或者window,为全局变量。这个打错了的案例只是声明了个空对象而已。输出也是空对象{}
。
10. 当我们这样做时会发生什么?
function bark() {
console.log("Woof!");
}
bark.animal = "dog";
这道题很有趣,正解是没什么问题,正常执行。因为js中的函数也是一种特殊的对象,加一个属性也没什么问题。而我的思路比较特殊,考虑到如果没有赋值单独读取的话应该会返回undefined
,对undefined
进行赋值应该不会出问题。不过还是领会函数的正确本质比较重要。
11. 下面代码的输出是什么?
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const member = new Person("Lydia", "Hallie");
Person.getFullName = () => this.firstName + this.lastName;
console.log(member.getFullName());
这道题没做出来,以为还是箭头函数this指代的问题。但实际上答案应该是 TypeError 。原因引用原作者:
您不能像使用常规对象那样向构造函数添加属性。 如果要一次向所有对象添加功能,则必须使用原型。 所以在这种情况下应该这样写:
Person.prototype.getFullName = function () { return
${this.firstName} ${this.lastName}; }
这样会使member.getFullName()
是可用的,为什么样做是对的? 假设我们将此方法添加到构造函数本身。 也许不是每个Person
实例都需要这种方法。这会浪费大量内存空间,因为它们仍然具有该属性,这占用了每个实例的内存空间。 相反,如果我们只将它添加到原型中,我们只需将它放在内存中的一个位置,但它们都可以访问它!————引自原文
P.S.: javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用。 也就是说通过protocol属性,我们可以对函数原型的属性和方法进行操作。这道题和上一题的区别是:上一道题是常规对象(一个普通的函数),而这个是个构造函数。
12. 下面代码的输出是什么?
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const lydia = new Person("Lydia", "Hallie");
const sarah = Person("Sarah", "Smith");
console.log(lydia);
console.log(sarah);
答案是 Person {firstName: "Lydia", lastName: "Hallie"}
, undefined
。
第一个可以正常输出没有任何疑问,第二个的声明问题在于没有用new
,(this真的是高频考点啊)如果没有用new,这个对象就归属于全局,此时的this是global或者window,sarah根本没有被赋值,自然是undefined。
12. 事件传播的三个阶段是什么??
答案: 在捕获阶段,事件通过父元素向下传递到目标元素。 然后它到达目标元素,冒泡开始。
案例:假设一个弹出登录窗口的页面,父元素为一个半透明遮罩,绑定点击事件为隐藏自身。子元素为登录窗口本身,上面有提交按钮。实际上当提交按钮被点击的时候,提交后父元素点击事件也被触发,这个过程叫做冒泡。 事件的对象有一个stopPropagation()方法可以阻止事件冒泡 。事件捕获是指不太具体的节点应该更早的接收到事件,而最具体的节点应该最后接收到事件 , stopPropagation()方法既可以阻止事件冒泡,也可以阻止事件捕获,也可以阻止处于目标阶段。 有点迷惑的话,有一张图解:
感觉比较有趣,有空单开一篇文章分析其机制好了。
13. 所有对象都有原型?
答: 除基础对象外,所有对象都有原型。 基础对象的原型是null
。
14. 下面代码的输出是什么?
function sum(a, b) {
return a + b;
}
sum(1, "2");
答案为"12",因为在这里发生了 隐式类型转换,1被隐式转换成了字符串。js中加号可以用来连接字符串,所以输出了"12"
15. 下面代码的输出是什么?
let number = 0;
console.log(number++);
console.log(++number);
console.log(number);
答案为:0,2,2,这道题感觉是C语言考试的相似题啊,自增放在前面是先自增再返回,放在后面是先返回再自增。所以过程是“返回=>自增=>自增=>返回=>返回”
16. 下面代码的输出是什么?
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`;
答案: ["", "is", "years old"] Lydia 21
因为如果使用标记的模板字符串,则第一个参数的值始终是字符串值的数组。 其余参数获取传递到模板字符串中的表达式的值。(这样做的目的是什么???)
17. 下面代码的输出是什么?
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 });
这道题很有趣,因为对于对象来说,比较的不是他们的值,而是引用的地址hhhh,所以无论是相等还是严格相等都会判断false。(天坑啊)答案为:Hmm.. You don't have an age I guess
,如果想比较值的话大概只能把值读取出来进行比较了。
18. 下面代码的输出是什么?
function getAge(...args) {
console.log(typeof args);
}
getAge(21);
答案为: "object"
,扩展运算符(... args
)返回一个带参数的数组。 数组是一个对象,因此typeof args
返回object
。
P.S.:** 对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象**之中 (是对象啊!)
20. 下面代码的输出是什么?
function getAge() {
"use strict";
age = 21;
console.log(age);
}
getAge();
答案为: ReferenceError
,是"use strict";
的锅,严格模式下不可以不经声明就使用变量,age属性不会自动变成全局变量的。
21. 下面代码的输出是什么?
const sum = eval("10*10+5");
答案为:105——即使是是字符串eval也会对表达式进行求值的。(当然需要是个表达式)
22. cool_secret可以访问多长时间?
sessionStorage.setItem("cool_secret", 123);
其实就是在问sessionStorage的存储时间,事实上,sessionStorage中的数据关闭选项卡就没有了, localStorage 中的数据将会永远存在。(TODO:写篇这个的笔记吧。)
23. 下面代码的输出是什么?
var num = 8;
var num = 10;
console.log(num);
答案:10,var重复声明变量会直接覆盖。
24. 下面代码的输出是什么?
const obj = { 1: "a", 2: "b", 3: "c" };
const set = new Set([1, 2, 3, 4, 5]);
obj.hasOwnProperty("1");
obj.hasOwnProperty(1);
set.has("1");
set.has(1);
答案:true,true,false,true,对象的键值不指定字符串也是字符串,所以可以都返回true,而set不行。
25. 下面代码的输出是什么?
const obj = { a: "one", b: "two", a: "three" };
console.log(obj);
答案: { a: "three", b: "two" }
对象中相同的键的值会被替换,但是会保留第一次出现的位置。(想的简单点不就好了吗… :-))
(——未完待续)