Object 对象的相关方法及严格模式

一、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等)。使用这些词作为变量名将会报错。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值