JavaScript 继承机制的设计思想?
- 原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享
prototype 属性有什么作用?
- 构造函数生成实例的时候,prototype 属性会自动成为实例对象的原型;
- 在构造函数的原型对象上添加一个属性,则这个构造函数生成的实例都会共享这个属性;
- 实例对象本身没有某个属性或方法的时候,它会到原型中去找这个属性或方法
so,总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。
什么是原型链?
- 每个对象拥有一个原型对象,通过 proto指针指向上一个原型 ,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。
- 这种关系被称为原型链 (prototype chain),通过原型链一个对象会拥有定义在其他对象中的属性和方法。
原型链的尽头是什么?
- null, 没有任何属性和方法,也没有自己的原型
原型链中对象属性的查找规则是什么?
- 先寻找对象本身的属性
- 如果找不到,就到它的原型去找
- 如果还是找不到,就到原型的原型去找
- 直到最顶层的Object.prototype还是找不到,则返回undefined
constructor的属性有什么作用?
- 可以得知某个实例对象,到底是哪一个构造函数产生的
- 有了constructor属性,就可以从一个实例对象新建另一个实例
为什么修改原型对象时,一般要同时修改constructor属性的指向?
function A() {}
function B() {}
A.prototype.constructor === A; // true
A.prototype = B;
A.prototype.constructor === A; // false
A.prototype.constructor === Function; // true
如上,修改了A.prototype以后,constructor属性的指向就变了,因为A的新原型B是一个函数,而函数的constructor属性指向Function构造函数,所以,修改原型对象时,一般要同时修改constructor属性的指向。
一般可以像下面这么写:
C.prototype = {
constructor: C,
method1: function (...) { ... },
// ...
};
// 或更好的写法
C.prototype.method1 = function (...) { ... };
tips:
- 可以通过name属性,从实例得到构造函数的名称
A.prototype.constructor = A;
var a = new A();
a.constructor.name // "A"
instanceof 和 typeof 判断类型有什么区别?
- instanceof表示对象是否为某个构造函数的实例
- instanceof运算符只能用于对象,不适用原始类型的值; 而typeof适用于判断原始类型
- 结合instanceof和typeof运算符可以判断一个值是否为非null的对象
null instanceof Object
// false
typeof null
// "object"
如何实现构造函数的继承?
- 在子类的构造函数中,调用父类的构造函数
- 让子类的原型指向父类的原型,或者让子类的原型等于一个父类的实例
例如下面是实现Sub继承父类Super的过程:
function Super() {};
function Sub(value) {
Super.call(this); // 子类内部调用父类的构造函数
this.prop = value;
}
Sub.prototype = Object.create(Super.prototype); // 让子类的原型指向父类的原型
Sub.prototype.constructor = Sub; // 别忘了同时修改constructor属性
Sub.prototype.method = '...';
立即执行函数的作用?
- 封装私有变量,创建一个独立的作用域,避免变量污染
怎么获取实例对象obj的原型对象?
- obj.proto
- obj.constructor.prototype
- Object.getPrototypeOf(obj)
严格模式下有哪些规则?
- 不可以设置字符串的length属性,会报错
- 对只读属性赋值,或者删除不可配置(non-configurable)属性都会报错
- 对一个只有取值器(getter)、没有存值器(setter)的属性赋值,会报错
- 对禁止扩展的对象添加新属性,会报错
- 使用eval或者arguments作为标识名,将会报错
- 函数不能有重名的参数
- 禁止八进制的前缀0表示法,整数第一位为0,将报错
- 全局变量必须显式声明
- 禁止 this 关键字指向全局对象
- 函数内部不得使用fn.caller、fn.arguments,否则会报错
- 函数内部使用arguments.callee、arguments.caller将会报错
- 严格模式下无法删除变量,如果使用delete命令删除一个变量,会报错。只有对象的属性,且属性的描述对象的configurable属性设置为true,才能被delete命令删除
- 禁止使用 with 语句
- eval所生成的变量只能用于eval内部
- arguments 不再追踪参数的变化
- 不允许在非函数的代码块内声明函数
- 新增了一些保留字(implements、interface、let、package、private、protected、public、static、yield等)。使用这些词作为变量名将会报错