1、js运行之前,会把带有var和function关键字的事先声明,但不会赋值
1 | alert(a) //function a(){alert(10)} |
1 | alert(a) //undefined |
考点:第一变量和函数声明提前,第二函数声明优先于变量声明!
2、下面代码输出什么?
1 | var a=0; |
1 | var a=0; |
考点: 函数内部如果用var声明了相同名称的外部变量,函数将不再向上寻找;这里在函数体内,参数a的优先级高于变量a
3、严格模式下,以下程序的输出是什么:
1 | (function(){ |
答案: a defined? false,b defined? true
理解这道题的核心在于如何理解var a = b = 3这句话,实际上这句话等于
1 | var a; |
这样子,实际上,b是声明在了全局变量中(编译器在预编译帮你声明了,然而在严格模式下是不行的) a是局部变量,所以在函数之外是没有定义的。
4、数组的filter,以下输出结果是什么:
1 | var arr = [1,2,3]; |
解析: 是的,答案的确是[],不是[undefined x 7]。当数组中都是undefined时,数组就是空,或者说[empty x 7] === []。
5、写一个sum方法,可以实现以下两种调用方式
1 | console.log(sum(2,3)) //5 |
答案:
1 | //方法1 |
6、+ - 运算符之惑:
1 | console.log(1 + "2" + "2"); //"122" |
核心是以下几点:
- “- +”会隐式转换为Number类型
- 当”+”作为运算符出现在String类型前时,会认为需要字符串拼接,因此会隐式转换为String
- Number包含一个特殊的类型NaN,当对非数字进行Number转换时,会变为这个。
7、堆栈溢出之谜
下面的代码将会造成栈溢出,请问如何优化,不改变原有逻辑:
1 | var list = readHugeList(); |
答案:
1 | var nextListItem = function() { |
首先必须搞清楚,堆栈溢出的原因:
原因是每次执行代码时,都会分配一定尺寸的栈空间(Windows系统中为1M),每次方法调用时都会在栈里储存一定信息(如参数、局部变量、返回值等等),这些信息再少也会占用一定空间,成千上万个此类空间累积起来,自然就超过线程的栈空间了。那么如何解决此类问题?
这里介绍两个思路解决此问题: 1、异步,2、闭包
显然,这里就是使用的第一种方法,闭包。为什么使用setTimeout就可以解决问题?我们看下与没用之前的差别。如果没有使用setTimeout,那么函数将在大数据前不断的回调,直到最后走到重点,最初的函数才运行结束,释放内存。 但是如果使用了setTimeout,我们知道它是异步的,即使设置了时间为0,它也允许先执行下面的内容,可以释放堆栈,从而避免堆栈溢出的问题。 换言之,加了setTimeout,nextListItem函数被压入事件队列,函数可以退出,因此每次会清空调用堆栈。
8、你真的懂对象(Object)的key吗?
1 | var a={}, |
原因是什么呢? 这里了解ES6新的数据类型 map 的应该就会意识到了,没错,对象的 key 值是只允许 String 类型的,这也是为什么引入了 map 数据类型了。 好了,那如果把一个对象作为 key 值,就会调用 toString 方法了。
Object.prototype.toString(obj) 会得到什么呢?没错`[object Object]。 那所以
1 | a[b] ==> a["[object Object"] = 123; |
9、回文判断
请做一个回文判断的函数,判断是否是回文
这里主要考虑了一个健壮性的问题,多了一个正则来检测:
1 | function check(str) { |
10、下面程序的输出结果是?
1 | var length = 10; |
这个我做错在第二个输出上,其实对this了解后就知道,第一个输出10应该是很显然的:虽然在程序执行时,使用了obj.method方法,让this指向了obj,但是真正的函数执行在函数体内部,也即当fn()执行的时候,this是指向window的,所以第一次执行结果是10
那么这里第二次执行arguments[0]为什么结果是2?
分析下在method(fn,1)执行时,经历了什么: 首先两个参数fn和1会被放入arguments中,在arguments中第一个参数就是我们传入的函数;接下来fn执行,此时this没有绑定因此指向window,输出10。 然而到了arguments0这一句,相当于把arguments[0]中的第一个参数拿来执行, 效果如下:
1 | arguments[0]() //执行,等同于下面的 |
arguments.length就是它本身的长度(arguments是一个类数组,具有length属性),因此输出2
11、try..catch程序的输出结果
1 | (function () { |
输出结果:
1 | 1 |
我们都知道var是在预编译阶段会有一个变量提升,这种类型很容易解决,但是当遇到在catch(x)中与已有变量重名的情况,一定要区分两者之间的关系。
用变量提升的方法,把程序重写并分析如下:
1 | (function () { |
这样子就很清晰,之后注意预编译的过程,把变量和函数定义进行提升后,进行分析,会清楚很多
12、下面程序的输出
1 | var x = 21; |
函数内部变量提升。 相当于:
1 | var x = 21; |
13、 运算符考点: 下面程序输出是什么?
1 | console.log(1 < 2 < 3); |
核心在于 js 怎么去解析 < 和 > 运算符。 在JS中,这种运算符是从左向右运算的,所以 3>2>1 就被转换成了 true>1,而 true 的值是 1,接着比较 1>1 就返回 false 了。
14、parseInt (val, radix) :两个参数,val 值,radix 基数(就是多少进制转换)
1 | ["1", "2", "3"].map(parseInt) |
- parseInt(‘1’, 0); // radix为0时,使用默认的10进制。
- parseInt(‘2’, 1); // radix值在2-36,无法解析,返回NaN
- parseInt(‘3’, 2); // 基数为2,2进制数表示的数中,最大值小于3,无法解析,返回NaN
15、IEEE 754标准中的浮点数并不能精确地表达小数
1 | var two = 0.2 |
1 | // 巩固: |
16、下面代码的输出是什么?
1 | const shape = { |
答案: 20 and NaN
请注意,diameter是普通函数,而perimeter是箭头函数。
对于箭头函数,this关键字指向是它所在上下文(定义时的位置)的环境,与普通函数不同! 这意味着当我们调用perimeter时,它不是指向shape对象,而是指其定义时的环境(window)。没有值radius属性,返回undefined。
17、哪个选项是不正确的?
1 | const bird = { |
- A: mouse.bird.size
- B: mouse[bird.size]
- C: mouse[bird[“size”]]
- D: All of them are valid
答案: A
在JavaScript中,所有对象键都是字符串(除了Symbol)。尽管有时我们可能不会给定字符串类型,但它们总是被转换为字符串。
JavaScript解释语句。当我们使用方括号表示法时,它会看到第一个左括号[,然后继续,直到找到右括号]。只有在那个时候,它才会对这个语句求值。
mouse [bird.size]:首先它会对bird.size求值,得到small。 mouse [“small”]返回true。
但是,使用点表示法,这不会发生。 mouse没有名为bird的键,这意味着mouse.bird是undefined。 然后,我们使用点符号来询问size:mouse.bird.size。 由于mouse.bird是undefined,我们实际上是在询问undefined.size。 这是无效的,并将抛出Cannot read property “size” of undefined。
18、下面代码的输出是什么?
1 | let a = 3; |
答案:true false false
new Number()是一个内置的函数构造函数。 虽然它看起来像一个数字,但它并不是一个真正的数字:它有一堆额外的功能,是一个对象。
当我们使用==运算符时,它只检查它是否具有相同的值。 他们都有3的值,所以它返回true。
译者注:== 会引发隐式类型转换,右侧的对象类型会自动拆箱为 Number 类型。
然而,当我们使用===操作符时,类型和值都需要相等,new Number() 不是一个数字,是一个对象类型。两者都返回 false。
19、下面代码的输出是什么?
1 | class Chameleon { |
答案: TypeError
colorChange 方法是静态的。 静态方法仅在创建它们的构造函数中存在,并且不能传递给任何子级。 由于freddie是一个子级对象,函数不会传递,所以在 freddie 实例上不存在 freddie 方法:抛出 TypeError。
20、当我们这样做时会发生什么?
1 | function bark() { |
- A: Nothing, this is totally fine!
- B: SyntaxError. You cannot add properties to a function this way.
- C: undefined
- D: ReferenceError
答案: A
这在JavaScript中是可能的,因为函数也是对象!(原始类型之外的所有东西都是对象)
函数是一种特殊类型的对象。您自己编写的代码并不是实际的函数。 该函数是具有属性的对象,此属性是可调用的。
21、下面代码的输出是什么?
1 | function Person(firstName, lastName) { |
- A: TypeError
- B: SyntaxError
- C: Lydia Hallie
- D: undefined undefined
答案: A
您不能像使用常规对象那样向构造函数添加属性。 如果要一次向所有对象添加功能,则必须使用原型。 所以在这种情况下应该这样写:
1 | Person.prototype.getFullName = function () { |
这样会使 member.getFullName() 是可用的,为什么样做是对的? 假设我们将此方法添加到构造函数本身。 也许不是每个 Person 实例都需要这种方法。这会浪费大量内存空间,因为它们仍然具有该属性,这占用了每个实例的内存空间。 相反,如果我们只将它添加到原型中,我们只需将它放在内存中的一个位置,但它们都可以访问它!
22、所有对象都有原型吗?
不是的,础对象指原型链终点的对象。基础对象的原型是null。
23、下面代码的输出是什么?
1 | function getPersonInfo(one, two, three) { |
- A: Lydia 21 [“”, “is”, “years old”]
- B: [“”, “is”, “years old”] Lydia 21
- C: Lydia [“”, “is”, “years old”] 21
答案: B
如果使用标记的模板字符串,则第一个参数的值始终是字符串值的数组。 其余参数获取传递到模板字符串中的表达式的值!
24、下面代码的输出是什么?
1 | const sum = eval("10*10+5"); |
- A: 105
- B: “105”
- C: TypeError
- D: “10*10+5”
答案: A
eval会为字符串传递的代码求值。 如果它是一个表达式,就像在这种情况下一样,它会计算表达式。 表达式为10 * 10 + 5计算得到105。
25、下面代码的输出是什么?
1 | const obj = { 1: "a", 2: "b", 3: "c" }; |
- A: false true false true
- B: false true true true
- C: true true false true
- D: true true true true
答案: C
所有对象键(不包括Symbols)都会被存储为字符串,即使你没有给定字符串类型的键。 这就是为什么obj.hasOwnProperty(’1’)也返回true。
上面的说法不适用于Set。 在我们的Set中没有“1”:set.has(’1’)返回false。 它有数字类型1,set.has(1)返回true。
26、下面这些值哪些是假值?
1 | 0; |
- A: 0, ‘’, undefined
- B: 0, new Number(0), ‘’, new Boolean(false), undefined
- C: 0, ‘’, new Boolean(false), undefined
- D: 所有都是假值
答案: A
JavaScript中只有6个假值:
- undefined
- null
- NaN
- 0
- ‘’ (empty string)
- false
- 函数构造函数,如 new Number 和 new Boolean 都是真值。