原型相关的面试题
如何准确判断一个变量是不是数组
使用 变量 instanceof Array
手写一个简易的jQuery,考虑插件和扩展性
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector)
const length = result.length
for (let i = 0; i < length; i++) {
this[i] = result[i]
}
this.length = length
this.selector = selector
}
get(index) {
return this[index]
}
each(fn) {
for (let i = 0; i < this.length; i++) {
const elem = this[i]
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false)
})
}
// 扩展很多 DOM API
}
// 插件
jQuery.prototype.dialog = function (info) {
alert(info)
}
// “造轮子”
class myJQuery extends jQuery {
constructor(selector) {
super(selector)
}
// 扩展自己的方法
addClass(className) {
}
style(data) {
}
}
// const $p = new jQuery('p')
// $p.get(1)
// $p.each((elem) => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))
class 的原型本质,怎么理解?
JavaScript 的 Class 概念其实是语法糖,JavaScript 仍是基于原型的,而 Java/C++ 等则是基于类的。
原型
每个 class 都有显式原型 prototype
每个实例都有隐式原型 __proto__
实例的 __proto__ 指向对应 class 的 prototype
虽然实例通过 __proto__ 获取原型,但规范最初是使用[[prototype]]去访问原型的。
原型链

基于原型的属性/方法查询规则
先在实例自身属性和方法中寻找,如果没有找到则在 实例的 __proto__ 中查找(原型链自自身向上查找)
补充内容
Class
首先,认清 Class 是用作构造器/构造函数和操作符 new 的语法糖(即简化),类在 JavaScript 中是函数。
当 new User("John") 被调用:
一个新对象被创建。constructor 使用给定的参数运行,并为其分配 this.name。
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// 佐证:User 是一个函数
alert(typeof User); // function
class User {...} 构造实际上做了如下的事儿:
创建一个名为 User 的函数,该函数成为类声明的结果。该函数的代码来自于 constructor 方法(如果我们不编写这种方法,那么它就被假定为空)。
在 F.prototype 中存储类中的方法,例如 User.prototype 中的 sayHi。

class又不仅仅是语法糖:
- 首先,通过 class 创建的函数具有特殊的内部属性标记 [[FunctionKind]]:"classConstructor"。因此,它与手动创建并不完全相同。编程语言会在许多地方检查该属性。例如,与普通函数不同,必须使用 new 来调用它:
class User {
constructor() {}
}
alert(typeof User); // function
User(); // Error: Class constructor User cannot be invoked without 'new'
- 此外,大多数 JavaScript 引擎中的类构造器的字符串表示形式都以 “class…” 开头
class User {
constructor() {}
}
alert(User); // class User { ... }
类方法不可枚举。 类定义将 "prototype" 中的所有方法的 enumerable 标志设置为 false。这很好,因为如果我们对一个对象调用 for..in 方法,我们通常不希望 class 方法出现。
类总是使用 use strict。 在类构造中的所有代码都将自动进入严格模式。
Class 继承
扩展另一个类的语法是:class Child extends Parent
super() 语法,用于:
- 执行 super.method(...) 来调用一个父类方法。
- 执行 super(...) 来调用一个父类 constructor(只能在我们的 constructor 中)。继承类的 constructor 必须调用 super(...),并且 (!) 一定要在使用 this 之前调用。
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
stop() {
this.speed = 0;
alert(`${this.name} stands still.`);
}
}
class Rabbit extends Animal {
hide() {
super();
alert(`${this.name} hides!`);
}
stop() {
super.stop(); // 调用父类的 stop
this.hide(); // 然后 hide
}
}
let rabbit = new Rabbit("White Rabbit");
rabbit.stop(); // White Rabbit 停止了。White rabbit hide 了!
如果一个类扩展了另一个类并且没有 constructor,那么将生成下面这样的“空” constructor:
class Rabbit extends Animal {
// 为没有自己的 constructor 的扩展类生成的
constructor(...args) {
super(...args);
}
}
本文使用 mdnice 排版