原生 JavaScript 知识体系
JavaScript 基础概念、原型、闭包、异步等常见考点
问题
谈谈你对原生 JavaScript 的了解程度。
解答
数据类型
JavaScript 有 8 种数据类型:
// 原始类型(7种)
const str = 'hello'; // string
const num = 42; // number
const bool = true; // boolean
const n = null; // null
const u = undefined; // undefined
const sym = Symbol('id'); // symbol
const big = 9007199254740991n; // bigint
// 引用类型(1种)
const obj = { name: 'Tom' }; // object(包括数组、函数、日期等)
// 类型判断
typeof str; // 'string'
typeof null; // 'object'(历史遗留 bug)
Array.isArray([]); // true
obj instanceof Object; // true
// 准确判断类型
Object.prototype.toString.call([]); // '[object Array]'
Object.prototype.toString.call(null); // '[object Null]'
作用域与闭包
// 作用域链:内部函数可以访问外部变量
function outer() {
const x = 10;
function inner() {
const y = 20;
console.log(x + y); // 可以访问外部的 x
}
return inner;
}
// 闭包:函数记住并访问其词法作用域
function createCounter() {
let count = 0; // 私有变量
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; }
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2
原型与继承
// 原型链
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const tom = new Person('Tom');
tom.sayHello(); // Hello, I'm Tom
// 原型链查找
tom.hasOwnProperty('name'); // true(自身属性)
tom.hasOwnProperty('sayHello'); // false(原型上的属性)
// ES6 class 语法
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks`);
}
}
const dog = new Dog('Rex');
dog.speak(); // Rex barks
this 指向
const obj = {
name: 'Tom',
// 普通函数:this 取决于调用方式
greet() {
console.log(this.name);
},
// 箭头函数:this 继承外层作用域
greetArrow: () => {
console.log(this.name); // undefined(指向全局)
},
// 延迟调用中的 this
delayGreet() {
// 错误:this 丢失
setTimeout(function() {
console.log(this.name); // undefined
}, 100);
// 正确:箭头函数保持 this
setTimeout(() => {
console.log(this.name); // Tom
}, 100);
}
};
// 显式绑定 this
const greet = obj.greet;
greet.call(obj); // Tom
greet.apply(obj); // Tom
greet.bind(obj)(); // Tom
异步编程
// 1. 回调函数
function fetchData(callback) {
setTimeout(() => {
callback('data');
}, 1000);
}
// 2. Promise
function fetchDataPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('data');
}, 1000);
});
}
fetchDataPromise()
.then(data => console.log(data))
.catch(err => console.error(err));
// 3. async/await
async function getData() {
try {
const data = await fetchDataPromise();
console.log(data);
} catch (err) {
console.error(err);
}
}
// 并行执行
async function parallel() {
const [a, b] = await Promise.all([
fetchDataPromise(),
fetchDataPromise()
]);
return [a, b];
}
事件循环
console.log('1'); // 同步
setTimeout(() => {
console.log('2'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务
});
console.log('4'); // 同步
// 输出顺序:1 -> 4 -> 3 -> 2
// 执行顺序:同步代码 -> 微任务 -> 宏任务
ES6+ 常用特性
// 解构赋值
const { name, age = 18 } = { name: 'Tom' };
const [first, ...rest] = [1, 2, 3];
// 展开运算符
const merged = { ...obj1, ...obj2 };
const combined = [...arr1, ...arr2];
// 可选链和空值合并
const city = user?.address?.city ?? 'Unknown';
// Map 和 Set
const map = new Map([['key', 'value']]);
const set = new Set([1, 2, 2, 3]); // {1, 2, 3}
// Proxy
const proxy = new Proxy(target, {
get(obj, prop) {
return prop in obj ? obj[prop] : 'default';
},
set(obj, prop, value) {
obj[prop] = value;
return true;
}
});
关键点
- 类型判断:
typeof对null返回object,用Object.prototype.toString.call()更准确 - 闭包:函数可以记住创建时的词法作用域,常用于数据私有化
- 原型链:对象通过
__proto__链接到原型,属性查找沿链向上 - this 绑定:普通函数看调用方式,箭头函数看定义位置
- 事件循环:同步代码 → 微任务(Promise)→ 宏任务(setTimeout)
目录