一、Object 对象的相关方法
Object.getPrototypeOf方法返回参数对象的原型。这是获取原型对象的标准方法。
实例对象f的原型是构造函数的prototype属性
几种特殊对象的原型
// 空对象的原型是 Object.prototype
Object.getPrototypeOf({}) === Object.prototype // true
// Object.prototype 的原型是 null
Object.getPrototypeOf(Object.prototype) === null // true
// 函数的原型是 Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true
1.1Object.setPrototypeOf方法
为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);
Object.getPrototypeOf(a) === b // true
a.x // 1
new命令可以使用Object.setPrototypeOf方法模拟。
var F = function () {
this.foo = 'bar';
};
var f = new F();
// 等同于
var f = Object.setPrototypeOf({}, F.prototype);
F.call(f);
第一步,将一个空对象的原型设为构造函数的prototype属性(上例是F.prototype);第二步,将构造函数内部的this绑定这个空对象,然后执行构造函数,使得定义在this上面的方法和属性(上例是this.foo),都转移到这个空对象上
1.2Object.create()
生成实例对象的常用方法是,使用new命令让构造函数返回一个实例
Object.create()方法,该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性
// 原型对象
var A = {
print: function () {
console.log('hello');
}
};
// 实例对象
var B = Object.create(A);
Object.getPrototypeOf(B) === A // true
B.print() // hello
B.print === A.print // true
Object.create()方法可以用下面的代码代替
if (typeof Object.create !== 'function') {
Object.create = function (obj) {
function F() {}
F.prototype = obj;
return new F();
};
}
Object.create()方法的实质是新建一个空的构造函数F,然后让F.prototype属性指向参数对象obj,最后返回一个F的实例,从而实现让该实例继承obj的属性
三种方式生成的新对象是等价的。
var obj1 = Object.create({});
var obj2 = Object.create(Object.prototype);
var obj3 = new Object();
如果想要生成一个不继承任何属性(比如没有toString()和valueOf()方法)的对象,可以将Object.create()的参数设为null
Object.create()方法的时候,必须提供对象原型,即参数不能为空,或者不是对象,否则会报错。
在原型上添加或修改任何方法,会立刻反映在新对象之上。
Object.create()方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性。
Object.create()方法生成的对象,继承了它的原型对象的构造函数。
1.3 isPrototypeOf方法
用来判断该对象是否为参数对象的原型。
只要实例对象处在参数对象的原型链上,isPrototypeOf方法都返回true。
Object.prototype.isPrototypeOf({}) // true
Object.prototype.isPrototypeOf([]) // true
Object.prototype.isPrototypeOf(/xyz/) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false
由于Object.prototype处于原型链的最顶端,所以对各种实例都返回true,只有直接继承自null的对象除外
1.4 __proto_属性
实例对象的__proto__属性(前后各两个下划线),返回该对象的原型。该属性可读写
var obj = {};
var p = {};
obj.__proto__ = p;
Object.getPrototypeOf(obj) === p // true
通过__proto__属性,将p对象设为obj对象的原型。
__proto__属性只有浏览器才需要部署,其他环境可以没有这个属性。它前后的两根下划线,表明它本质是一个内部属性,不应该对使用者暴露。因此,应该尽量少用
__proto__属性指向当前对象的原型对象,即构造函数的prototype属性
获取实例对象obj的原型对象,有三种方法。
obj.proto
obj.constructor.prototype
Object.getPrototypeOf(obj) 推荐使用
__proto__属性只有浏览器才需要部署,其他环境可以不部署。而obj.constructor.prototype在手动改变原型对象时,可能会失效。
在改变原型对象时,一般要同时设置constructor属性。
C.prototype = p;
C.prototype.constructor = C;
var c = new C();
c.constructor.prototype === p // true
1.5 getOwnPropertyNames()
Object.getOwnPropertyNames方法返回一个数组,成员是参数对象本身的所有属性的键名,不包含继承的属性键名。
Object.getOwnPropertyNames方法返回所有键名,不管是否可以遍历。只获取那些可以遍历的属性,使用Object.keys方法。
1.6Object.prototype.hasOwnProperty()
对象实例的hasOwnProperty方法返回一个布尔值,用于判断某个属性定义在对象自身true,还是定义在原型链上false
hasOwnProperty方法是 JavaScript 之中唯一一个处理对象属性时,不会遍历原型链的方法。
1.7 in 运算符和 for…in 循环
in运算符返回一个布尔值,表示一个对象是否具有某个属性。它不区分该属性是对象自身的属性,还是继承的属性。
获得对象的所有可遍历属性(不管是自身的还是继承的),可以使用for…in循环。
获得对象的所有属性(不管是自身的还是继承的,也不管是否可枚举),可以使用下面的函数。
function inheritedPropertyNames(obj) {
var props = {};
while(obj) {
Object.getOwnPropertyNames(obj).forEach(function(p) {
props[p] = true;
});
obj = Object.getPrototypeOf(obj);
}
return Object.getOwnPropertyNames(props);
}
1.8对象的拷贝
如果要拷贝一个对象,需要做到下面两件事情。
确保拷贝后的对象,与原对象具有同样的原型。
确保拷贝后的对象,与原对象具有同样的实例属性。
function copyObject(orig) {
var copy = Object.create(Object.getPrototypeOf(orig));
copyOwnPropertiesFrom(copy, orig);
return copy;
}
function copyOwnPropertiesFrom(target, source) {
Object
.getOwnPropertyNames(source)
.forEach(function (propKey) {
var desc = Object.getOwnPropertyDescriptor(source, propKey);
Object.defineProperty(target, propKey, desc);
});
return target;
}
简单的写法,是利用 ES2017 才引入标准的Object.getOwnPropertyDescriptors方法。
function copyObject(orig) {
return Object.create(
Object.getPrototypeOf(orig),
Object.getOwnPropertyDescriptors(orig)
);
}
二、严格模式
严格模式是从 ES5 进入标准的,主要目的有以下几个。
明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。
增加更多报错的场合,消除代码运行的一些不安全之处,保证代码运行的安全。
提高编译器效率,增加运行速度。
为未来新版本的 JavaScript 语法做好铺垫。
进入严格模式的标志,是一行字符串use strict。
'use strict';
严格模式可以用于整个脚本,也可以只用于单个函数。
(1) 整个脚本文件
use strict放在脚本文件的第一行,整个脚本都将以严格模式运行。
(2)单个函数
use strict放在函数体的第一行,则整个函数以严格模式运行。
严格模式的脚本在前,则合并后的脚本都是严格模式;如果正常模式的脚本在前,则合并后的脚本都是正常模式。这两种情况下,合并后的结果都是不正确的。这时可以考虑把整个脚本文件放在一个立即执行的匿名函数之中。
2.1显式报错
严格模式使得 JavaScript 的语法变得更严格,更多的操作会显式报错
- 严格模式下,对只读属性赋值,或者删除不可配置(non-configurable)属性都会报错。
- 只设置了取值器的属性不可写
- 禁止扩展的对象不可扩展
- eval、arguments 不可用作标识名
- 函数不能有重名的参数(正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,这属于语法错误。)
- 禁止八进制的前缀0表示法
- 全局变量必须显式声明。(严格模式下,变量都必须先声明,然后再使用)
- 禁止 this 关键字指向全局对象
严格模式下,函数直接调用时(不使用new调用),函数内部的this表示undefined(未定义),因此可以用call、apply和bind方法,将任意值绑定在this上面。正常模式下,this指向全局对象,如果绑定的值是非对象,将被自动转为对象再绑定上去,而null和undefined这两个无法转成对象的值,将被忽略
// 正常模式 function fun() { return this; } fun() // window fun.call(2) // Number {2} fun.call(true) // Boolean {true} fun.call(null) // window fun.call(undefined) // window // 严格模式 'use strict'; function fun() { return this; } fun() //undefined fun.call(2) // 2 fun.call(true) // true fun.call(null) // null fun.call(undefined) // undefined
- 函数内部不得使用fn.caller、fn.arguments
- 禁止使用 arguments.callee、arguments.caller
- 禁止删除变量 严格模式下无法删除变量,如果使用delete命令删除一个变量,会报错。只有对象的属性,且属性的描述对象的configurable属性设置为true,才能被delete命令删除。
'use strict'; var x; delete x; // 语法错误 var obj = Object.create(null, { x: { value: 1, configurable: true } }); delete obj.x; // 删除成功
2.2静态绑定
某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,必须在编译阶段就确定
- 禁止使用 with 语句
var a = obj.a; var b = obj.b; var c = obj.c; with(obj){ var a = a; var b = b; var c = c;
- 创设 eval 作用域 正常模式下,JavaScript 语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval作用域。 eval所生成的变量只能用于eval内部。
- arguments 不再追踪参数的变化
- 非函数代码块不得声明函数 ES5 环境会报错。 注意,如果是 ES6 环境,上面的代码不会报错,因为 ES6 允许在代码块之中声明函数
- 保留字 为了向将来 JavaScript 的新版本过渡,严格模式新增了一些保留字(implements、interface、let、package、private、protected、public、static、yield等)。使用这些词作为变量名将会报错。