js面试题
原文地址: https://juejin.im/post/6844903869378461710
1 let暂时性死区
function sayHi() {
console.log(name);
console.log(age);
var name = "Lydia";
let age = 21;
}
sayHi(); // undefined 和 ReferenceError
let是否存在变量提升?
let name = 'happy';
{
console.log(name);
// Uncaught ReferenceError: Cannot access 'name' before initialization
let name = 'lucky';
}
-
使用
var
关键字声明的变量,会进行变量声明的提升,默认为undefined
; -
使用
let(const)
声明的变量,会有变量提升,but没有变量声明的提升,在声明它们之前,它们是不可访问的,被称为:暂时性死区。
变量赋值可以分为三个阶段:
-
创建变量,在内存中开辟空间;
-
初始化变量,将变量初始化为undefined;
-
真正赋值。
关于 let
var
function
-
let
的【创建】过程被提升了,但是初始化没有提升; -
var
的【创建】和【初始化】都被提升了; -
function
的【创建】【初始化】【赋值】都被提升了
2 let
具有块作用域
for(var i=0; i<3; i++) {
setTimeout(()=>console.log(i),1);
}
for(let i=0; i<3; i++) {
setTimeout(()=>console.log(i),1);
}
-
var
关键字声明的变量i是全局变量;当调用setTimeout函数时,i已经被赋值为3; -
let(const)
关键字声明的变量i是具有块作用域的(块是{}之间的任何东西);每次迭代期间,i将被创建为一个新值,并且每个值都会存在于循环内的块级作用域。
3 箭头函数
const shape = {
radius: 10,
diameter() {
return this.radius*2;
},
perimeter: ()=> 2*Math.PI*this.radius
};
shape.diameter(); // 20
shape.perimeter(); // NaN
- 对于箭头函数,
this
关键字指向是它所在上下文的环境,与普通函数不同!
4 静态方法
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor;
}
constructor({newColor="green"}={}) {
this.newColor = newColor;
}
}
const freddie = new Chameleon({newColoe: "purple"});
freddie.colorChange("orange"); // TypeError
-
colorChange
方法是静态的。静态方法仅在创建它们的构造函数中存在,并且不能传递给任何子级。 -
freddie
是一个实例对象,函数不会传递,所以在freedie
实例上不存在colorChange
方法:抛出TypeError。
5 函数也是对象
function bark() {
console.log('Woof!");
}
bark.animal = "dog"; // 这段代码没毛病!
-
在js中,函数也是对象,原始类型之外的所有东西都是对象。
-
函数是一种特殊类型的对象,该函数是具有属性的对象,此属性是可调用的。
6 构造函数&原型对象
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const member = new Person("Davi", "Chow");
Person.getFullName = () => this.firstName + this.lastName;
console.log(member.getFullName()); // TypeError
-
不能像使用常规对象那样向构造函数添加属性,毕竟会浪费大量内存空间。
-
如果要一次向所有对象添加功能,则必须使用原型:只需要将它放在内存中的一个位置,但实例都可以访问它。
Person.prototype.getFullName = function() {
return `${this.firstName}` ${this.latName}`;
}
构造函数和原型对象的关系
-
每一个构造函数都有一个原型对象与之相关联
-
原型对象上面所有的成员(属性和方法)都可以被构造函数创建出来的(实例对象)所继承
-
构造函数
Person
原型对象Person.prototype
实例对象p1
-
构造函数 --> 原型对象
构造函数.prototype
-
原型对象 --> 构造函数
原型对象.constructor
-
实例对象 --> 原型对象
实例对象.__proto__
访问原型对象的方法
构造函数.prototype
实例对象.__proto__
Object.getPrototypeOf(实例对象)
原型链的基本结构
- 所有对象都是由构造函数创建出来的
- 所有的构造函数都有一个与之相关联的原型对象
- 原型对象也是对象 因此原型对象也是由构造函数创建出来的
- 原型对象的构造函数也有与之相关联的原型对象 因此原型对象的构造函数的原型对象也是对象
- 上面的对象也有构造函数...
- 以上就会形成一种链式的访问结构,称为原型链
对象访问规则
- 就近原则:
- 实例对象在访问成员时,先在自己身上查找,找到则直接使用
- 如果没有就查找原型对象,如果在原型对象上找到,则直接使用
- 原型对象上找不到就返回 undefined
7 构造函数&全局对象
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const p1 = new Person("Davi","Chow"); // Person {firstName:"Davi", lastName: "Chow"}
const p2 = Person("Davi","Chow"); // undefined
- 不使用
new
创建对象时构造函数中的this
指向的是全局对象,即global.firstName="Davi" global.lastName="Chow"
8 null
- 所有对象都有原型 -- false -- 除null外的所有对象都有原型
9 加法
function sum(a,b) {
return a + b;
}
sum(1, "2"); // 12
10 使用模板字符串传参
function f1(one, two, three) {
console.log(one);
console.log(two);
console.log(three);
}
const person = "Davi";
const age = "18";
f1`${person} is ${age} years old`;
// ["", "is", "years old"] Davi 18
- 使用标记的模板字符串,则第一个参数的值始终是字符串值的数组。其余参数获取传递到模板字符串中的表达式的值。
11 typeof
复杂对象
function getAge(...args) {
console.log(typeof args); // (...args)返回一个带参数的数组
}
getAge(21); // "object"
12 "use strict";
时不声明变量
function f1() {
"use strict";
age = 18;
console.log(age);
}
f1(); // ReferenceError
13 sessionStorage
&& localStorage
// cool_secret可以访问多长时间
sessionStorage.setItem("cool_secret", 123);
// 关闭选项卡后,将删除存储在sessionStorage中的数据
// cool_secret2可以访问多长时间
localStorage.setItem("cool_secret2",123);
// 数据将永远存在,除非调用localStorage.clear()
14 对象存储 && set存储
const obj = { 1: 'a', 2: 'b', 3: 'c' };
const set = new Set([1, 2, 3, 4, 5]);
obj.hasOwnProperty('1'); // true
obj.hasOwnProperty(1); // true
set.has('1'); // false
set.has(1); // true
- 所有对象键(不包括Symbols)都会被存储为字符串,即使没给定字符串类型的键
15 相同键名的对象
const obj = { a: 'one', b: 'two', a: 'three' };
console.log(obj); // {a: 'three', b: 'two'}
16 js全局执行上下文为你创建了两个东西:全局对象和this关键字
17 对象键 && 字符串
const a = {};
const b = { key: 'b' };
const c = { key: 'c' };
a[b] = 123; // const定义的对象能改变吗?
a[c] = 456;
console.log(a[b]); // 456
a[b.key] = 123;
a[c.key] = 456;
console.log(a[b.key]) // 123
a = { b:123, c: 456 }; // VM171:1 Uncaught TypeError: Assignment to constant variable.
-
console.log(a[b]); // 456
-
对象键自动转换为字符串。当将一个对象设置为对象a的键,其值为123
-
但是,当对象转为字符串时,它变成了[Object object]
-
所以
a[b] = a['Object object'] = 123
a[c] = a['Object object'] = 456
-
-
console.log(a[b.key]) // 123
-
因为对象是引用类型的,a中保存的仅是对象的指针
-
这就意味着,const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。
-
也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。
-
18 事件冒泡
<div onClick="console.log('first div')">
<div onClick="console.log('second div')">
<button onClick="console.log('button')">
Click!
</button>
</div>
</div>
<!-- 单击按钮时event.target -> button -->
<!-- button second div first div -->
-
导致事件的最深嵌套元素是事件的目标。可通过
event.stopPropagation
停止冒泡 -
在事件传播期间,有三个阶段: 捕获,目标,冒泡 。在默认情况下,事件处理程序在冒泡阶段执行(除非useCature设置为true),从最深的嵌套元素向外延申。
19 call && bind
const person = { name: 'Davi' };
function sayHi(age) {
console.log(`${this.name} is ${age}`);
}
sayHi.call(person, 12); // Davi is 12
sayHi.bind(person, 12); // function
-
call
和apply
借用其他对象的方法并且绑定方法中的this
,马上执行 -
bind
方法绑定方法中的this
,返回新的函数,不会立即执行
20 立即执行函数
function sayHi() {
return (() => 0)();
}
typeof sayHi(); // "number"
sayHi
函数返回立即调用的函数的返回值。该函数返回0,类型为数字。
21 假值 -- 下面哪些值是假值
0; // 假值
new Number(0); // 函数构造函数:真值
(""); // 假值
(" "); // 真值
new Boolean(false); // 函数构造函数:真值
undefined; // 假值
- js中只有6个假值
* undefined
* null
* NaN
* 0
* "" (empty string)
* false
22 空插槽
const numbers = [1, 2, 3];
numbers[10] = 11;
console.log(numbers); // (11) [1, 2, 3, empty × 7, 11]
- 当为数组中的元素设置一个超过数组长度的值时,js会创建一个名为“空插槽”的东西。这些位置的值实际上是
undefined
。
23 作用域
(() => {
let x, y;
try {
throw new Error();
} catch(x) {
(x = 1), (y = 2);
console.log(x); // 1
}
console.log(x); // undefined
console.log(y); // 2
})();
catch
块接收参数x。传递的参数与变量x不同,catch
块内的x是属于catch作用域的。
24 js中的所有内容都是原始类型和对象
-
js 只有原始类型和对象
-
原始类型是
boolean
,null
,undefined
,number
,string
,symbol
25 reduce
/*
* reduce((total, currentValue, currentIndex, arr)=>{}, initVal)
* 参数: 初始值|计算结束后的返回值 当前元素
*/
[[0, 1], [2, 3]].reduce((acc, cur) => {
return acc.concat(cur)
}, [1, 2]);
// acc = [1, 2] cur = [0, 1] -> acc = [1, 2, 0, 1]
// acc = [1, 2, 0, 1] cur = [2, 3] -> acc = [1, 2, 0, 1, 2, 3]
26 setInterval
的返回值
setInterval(() => console.log('Hi'), 1000);
// setInterval 返回一个唯一的id,此id可用于使用clearInterval()函数清除该定时器
27 扩展运算符
[..."Davi"];
// ["D", "a", "v", "i"]
- 字符串是可迭代的,扩展运算符将迭代的每个字符映射到一个元素
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)