undefined 和 null 有什么区别?
undefined:表示一个值未被定义。典型用法:
(1)变量被声明了,但没有赋值时,就等于 undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于 undefined。
(3)对象没有赋值的属性,该属性的值为 undefined。
(4)函数没有返回值时,默认返回 undefined。null:表示一个值被定义为“空值”。典型用法:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
解释 JavaScript 中的值和类型?
-
基本数据类型:
String
Number
Boolean
Null
Undefined
Symbol(ES6) -
复杂数据类型:
Object
JavaScript 的 typeof 返回值有哪些?
- "undefined"
- "boolean"
- "string"
- "number"
- "object" (如果这个值是对象或null)
- "function" (如果这个值是函数)
以下代码输出的结果是什么?
0.1 + 0.2 === 0.3
这段代码的输出是 false,这是由浮点数内部表示导致的。0.1 + 0.2 并不刚好等于 0.3,实际结果是 0.30000000000000004。解决这个问题的一个办法是在对小数进行算术运算时对结果进行舍入。
为什么在 JS 中比较两个相似的对象时返回 false?
let a = { a: 1 };
let b = { a: 1 };
let c = a;
console.log(a === b); // false
console.log(a === c); // true
JS 在比较对象时,是通过比较对象在内存中的存储地址。
因此, a 与 b 是放在不同的引用地址,返回 false;a 与 c 是存放在相同的引用地址,返回 true。
深浅拷贝?
深浅拷贝只是针对引用类型的,因为引用类型是存放在堆内存中,在栈地址有一个或者多个地址来指向堆内存的某一数据
-
浅拷贝:
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象,如果你修改了“副本”的值,那么原来的对象也会被修改let arr = [22, 44, 66, 88]; let co = arr; co[0] = 11; console.log(arr, co); // [11,44,66,88] [11,44,66,88]
深拷贝:
深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
深拷贝把要复制的对象所引用的对象都复制了一遍。如果你修改了“副本”的值,那么原来的对象不会被修改,两者是相互独立的。
如何在一行中计算多个表达式的值?
可以使用逗号运算符在一行中计算多个表达式。 它从左到右求值,并返回右边最后一个项目或最后一个操作数的值。
let x = 5;
x = (x++, x = addFive(x), x *= 2, x -= 5, x += 10);
function addFive(num) {
return num + 5;
}
因此,上述操作最终返回 27。
计算步骤如下:
x = 5;
x++:进行了自增,得到 x = 6;
x = addFive(x):传入参数 6,得到 x = 11;
x *= 2:进行了乘法操作,得到 x = 22;
x -= 5:进行减法操作,得到 x = 17;
x += 10:进行加法操作,得到 x = 27。
JavaScript 中的虚值是什么?如何检查是否为虚值?
-
虚值:可以简单理解为转换为布尔值变为 false 的值。
const falsyValues = ['', 0, null, undefined, NaN, false];
-
检查方法:
使用 Boolean 函数或者 !! 运算符。
如何理解 JS 中的 this 关键字?
-
介绍
this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。 -
绑定规则:
1. 默认绑定
:
独立函数的调用。可以把这条规则看作是无法应用其他规则的时的默认规则。function foo() { console.log(this.a) } var a = 2 foo() // 2
2. 隐式绑定
:
调用位置是否有上下文对象。function foo() { console.log(this.a) } var obj = { a: 2, foo: foo } obj.foo() // 2
3. 显示绑定
:
使用 call()、apply()、bind()绑定function foo() { console.log(this.a) } var obj = { a: 2 } foo.call(obj) // 2
4. new 绑定
:function foo(a) { this.a = a } var bar = new foo(2) console.log(bar.a) // 2
-
优先级:
4 > 3 > 2 > 1
事件委托是什么?
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
作用域是什么?
作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是为了对变量进行赋值,那么就会使用 LHS 查询。如果目的是为了获取变量的值,就会使用 RHS 查询。
什么是闭包?
-
定义:
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。function foo () { var a = 2 function bar() { console.log(a) } return bar } vra baz = foo() baz() // 2
原型链
-
定义:
每个实例对象( object )都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
解释一下变量的提升?
引擎会在解释 JavaScript 代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。
这个过程,就像把变量和函数声明从它们在代码中出现的位置被移动到了最上面,这个过程就叫做提升。
函数声明和变量声明都会被提升,优先级是函数会首先被提升,然后才是变量。
如何理解高阶函数?
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
一个简单的高阶函数定义:
function add(x, y, f) {
return f(x) + f(y);
}
如何区分声明函数和表达式函数?
区分函数声明和表达式最简单的方法是看function关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
什么是 IIFE(立即调用函数表达式)?
由于函数被包含在一对()括号内部,因此成为了一个表达式,通过在末尾加上另外一个()可以立即执行这个函数,比如(function foo() { .. })()。第一个()将函数编程表达式,第二个()执行了这个函数。
(
function foo() {
var a = 3
console.log(a)
}
)()