• 深度挖掘 Object 对象的使用


    介绍

    在javascript中, 数据类型主要分为原始类型和引用类型两种。而一切引用类型都来自于Object的拷贝。所有引用类型的原型链都可以追溯到 Object

    Object 构造函数属性

    JavaScript 内置的一些构造函数有 Object, Function, Number, String, Boolean, Array, RegExp 等等, 它们主要有两个共有的属性。

    • length 构造函数参数个数

    • prototype 构造函数原型对象

    Object原型链

    • Object.getPrototypeOf

    • Object.isPrototypeOf

    • Object.hasOwnProperty

    一切引用对象的原型都来自 Object.prototype

    测试各个数据类型的引用情况

    var a = {},

        b = [],

        c = function () {},

        d = Function,

        e = String;

    [a, b, c, d, e].forEach(function (val) {

        // all return true

        console.log(val instanceof Object);

    });

    每一个引用类型对象, 都含有一个原型属性__proto__, 它负责控制对象的原型属性和方法的查询使用

    创建无Object.prototype的原型链对象

    // method 1

    var obj1 = Object.create(null);

    // method 2

    var obj2 = {};

    Object.setPrototypeOf(obj2, null);

    // method 3

    var obj3 = {};

    obj3.__proto__ = null;

    [obj1, obj2, obj3].forEach(function (item) {

      console.log(item instanceof Object); // false

    });

    __proto__ 与 prototype

    __proto__ 隐式原型, prototype 显示原型. 

    实例对象通过隐式原型__proto__ 匹配找到对应的函数和属性. 而prototype是每一个构造函数都存在的一个属性。其中prototype 包含 constructor属性

    var o = {};

    '__proto__' in o; // return true, 说明 字面量对象存在一个隐式属性

    // 指定隐式属性

    function Foo () {}

    o.__proto__ = Foo.prototype;

    o instanceof Foo; // return true

    o instanceof Object; // return true

    设置隐藏原型属性

    var o = {};

    '__proto__' in o;

    function Foo () {}

    function Bar () {}

    Bar.prototype = new Foo();

    Bar.prototype.constructor = Bar;

    // 方法一, 直接设置 __proto__值

    o.__proto__ = Foo.prototype;

    console.log(o instanceof Foo); // return true;

    // 方法二, 使用 setPrototypeOf

    Object.setPrototypeOf(o, Bar.prototype); // 设置新原型

    console.log(o instanceof Bar); // return true;

    // 获取对象隐式属性

    Object.getPrototypeOf(o) === Bar.prototype; // return true

    // 检查原型 是否存在于对象属性的链中

    Bar.prototype.isPrototypeOf(o); // true

    Foo.prototype.isPrototypeOf(o); // true

    Object.prototype.isPrototypeOf(o); // true

    获取对象的实例属性

    • Object.keys

    • Object.getOwnPropertyNames

    // return []

    Object.keys(Object.prototype)

    // return ["constructor", "__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__", "isPrototypeOf", "propertyIsEnumerable", "toString", "valueOf", "__proto__", "toLocaleString"]

    Object.getOwnPropertyNames(Object.prototype)

    Object.keys 返回一个对象的实例可枚举属性, 如果使用了Object.defineProperty改变了对象的某个属性, 则无法通过Object.keys返回属性进行遍历属性, 也无法使用 for-in循环。

    var obj = {

        foo: function () {}

    };

    // return ['foo']

    Object.keys(obj);

    Object.defineProperty(obj, 'foo', {

       enumerable: false

    });

    // return []

    Object.keys(obj);

    // empty loop

    for (var name in obj) {

      console.log(name);

    }

    // return false

    obj.hasOwnProperty('foo');

    Object.getOwnPropertyNames() 返回自身实例属性名称, 无视enumerable: false

    var obj = {

        foo: function () {},

        bar: 'hello world'

    };

    Object.defineProperty(obj, 'foo', {

        // foo 已被定义, 所以需要显示设置 false

        enumerable: false

    });

    Object.defineProperty(obj, 'foo2', {

        value: function () {},

        // foo2 未被定义, 默认enumerable为false

        enumerable: true

    });

    // 'bar', 'foo2'

    Object.keys(obj);

    // 'foo', 'bar', 'foo2'

    Object.getOwnPropertyNames(obj);

    'foo' in obj // return true

    in 操作符, 检查属性是否在能够获取, 无视属性

    对象定义属性

    • Object.defineProperty

    • Object.getOwnPropertyDescriptor

    属性描述一般是由enumrable, value, get, set, configurable, writeable, value 组成, 其中 get, set 与 value 为互斥关系

    定义一个对象的描述

    var obj = Object.create(null);

    Object.defineProperty(obj, 'foo', {

        value: 'foo',

        configurable: false,

        writeable: false,

        enumerable: false

    });

    obj.foo = 'change foo';

    console.log(obj.foo); // still foo

    Object.defineProperty(obj, 'foo', {

        writeable: true,

        configurable: true

    });

    obj.foo = 'change foo 2';

    console.log(obj.foo); // still foo

    'foo' in obj; // return true

    • 一旦被定义了configureable: false, 那么后续再次 defineProperty时不会生效。

    • writeable: false 的情况下, 无法修改属性的 value 值

    属性描述中的 value 与 get, set

    var obj = Object.create(null);

    // throw Error 

    // Uncaught TypeError: Invalid property descriptor. 

    // Cannot both specify accessors and a value or writable attribute

    Object.defineProperty(obj, 'foo', {

        value: 'foo',

        set: function (val) {

            // this 指向 obj

            this._foo = val;

        },

        get: function () {

            return this._foo;

        }

    });

    因为 value 与 get, set 为互斥关系, 所以无法同时进行定义。

    writeable 与 get, set

    var obj = Object.create(null);

    Object.defineProperty(obj, 'foo', {

        // 失效

        writeable: false,

        set: function (val) {

            // this 指向 obj

            this._foo = val;

        },

        get: function () {

            return this._foo;

        }

    });

    obj.foo = 'abc'; // set()

    obj._foo === obj.foo  // return true

    console.log(obj.foo); // return 'abc'

    'foo' in obj // return true

    writeable 失效, 无法对属性做任何限制

    重复定义对象的属性

    var obj = Object.create(null);

    Object.defineProperty(obj, 'foo', {

        configurable: false,

        value: 'foo'

    });

    // Uncaught TypeError: Cannot redefine property: foo

    Object.defineProperty(obj, 'foo', {

        value: 'foo2'

    });

    一旦定义了configurable: false以后, 不允许再次定义 descriptor

    获取对象的属性描述

    var obj = Object.create(null);

    Object.defineProperty(obj, 'foo', {

        configurable: true,

        value: 'foo'

    });

    var descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');

    console.log(descrptor); // {value: "foo", writable: false, enumerable: false, configurable: false}

    // {foo: {value: "foo", writable: false, enumerable: false, configurable: false}}

    Object.getOwnPropertyDescriptors(obj);

    对象拷贝

    浅拷贝

    • Object.assign

    将候选的对象里的可枚举属性进行引用赋值, 支持多个候选的对象传递

    var o = {

      foo: 'foo',

      bar: 'bar'

    };

    Object.defineProperty(o, 'foo', {

      enumerable: false

    });

    var obj = Object.assign(Object.create(null), o, {

      o: o

    });

    /*

    obj = {

      bar: 'bar',

      o: {

        'foo': 'foo',

        'bar': 'bar'

      }

    }

    */

    obj.o === o; // return true;

    由于Object.assign 处于浅拷贝的关系, 所以返回的key 都为简单的引用方式.

    深度拷贝

    使用 对象系列化方式copy数据格式

    // 该方法只能拷贝基本数据类型

    var obj = {a: 1, b: 2, c: function () { console.log('hello world');}, d: {e: 3, f: 4}};

    JSON.parse(JSON.stringify(obj));

    自行实现深度拷贝

    function deepClone (obj) {

      var newObj;

      var isPlainObject = function (o) {

        return Object.prototype.toString.call(o) === '[object Object]';

      };

      var isArray = function (o) {

        return Object.prototype.toString.call(o) === '[object Array]';

      };

      if (isArray(obj)) {

        newObj = [];

        for (var i = 0, len = obj.length; i < len; i++) {

          newObj.push(deepClone(obj[i]));

        }

      } else if (isPlainObject(obj)) {

        newObj = {};

        for (var key in obj) {

          newObj[key] = deepClone(obj[key]);

        }

      } else {

        newObj = obj;

      }

      return newObj;

    }

    var o = {a: 1, b: [{c: 2, d: 3}]};

    var o2 = deepClone(o);

    o.b[0] !== o2.b[0]; // return true

    深度拷贝对象函数, 内置的对象和数组都被完整的拷贝出来。

    循环引用拷贝问题

    var o = {a: 1, b: 2};

    // 循环引用问题

    o.foo = o;

    // Uncaught TypeError: Converting circular structure to JSON

    JSON.stringify(o);

    // Uncaught RangeError: Maximum call stack size exceeded

    deepClone(o);

    // 将循环引用的key设置为不可枚举型

    Object.defineProperty(o, 'foo', {enumerable: false});

    // OK {"a":1,"b":2}

    JSON.stringify(o);

    避免重复循环引用的深度clone

    function deepClone (obj) {

      var objStack = [];

      var isPlainObject = function (o) {

        return Object.prototype.toString.call(o) === '[object Object]';

      };

      var isArray = function (o) {

        return Object.prototype.toString.call(o) === '[object Array]';

      };

      

      var isError = function (o) {

        return o instanceof Error;

      };

      function _deepClone (obj) {

        var newObj, cloneObj;

        if (isArray(obj) || isPlainObject(obj)) {

          // 对象重复引用

          if (objStack.indexOf(obj) === -1) {

            objStack.push(obj);

          } else {

            return new Error('parameter Error. it is exits loop reference');

          }

        }

        if (isArray(obj)) {

          newObj = [];

          for (var i = 0, len = obj.length; i < len; i++) {

            cloneObj = _deepClone(obj[i]);

            if (!isError(cloneObj)) {

              newObj.push(cloneObj);

            }

          }

        } else if (isPlainObject(obj)) {

          newObj = {};

          for (var key in obj) {

            cloneObj = _deepClone(obj[key]);

            if (!isError(cloneObj)) {

              newObj[key] = cloneObj;

            }

          }

        } else {

          newObj = obj;

        }

        return newObj;

      }

      return _deepClone(obj);

    }

    Object.create 创建对象实例

    一般创建对象有三种方式

    • 字面量方式 var o = {};

    • 构造函数方式 var o = new Object();

    • Object.create();

    Object.create 与 字面量创建的区别

    Object.create 可以指定创建的对象的隐式原型链 __proto__, 也可以创建空原型链的的对象

    var prototype = {

      foo: 'foo',

      name: 'prototoype'

    };

    var o = Object.create(prototype);

    var o2 = Object.create(null);

    o.__proto__ === prototype; // true

    '__protot__' in o2; // false

    Object.create 与构造函数的区别

    Object.create 直接进行开辟对象空间, 绑定隐式原型链属性。而构造函数创建对象除了上面的操作以外, 还会运行构造函数。

    利用 Object.create 与构造函数, 实现继承对象关系

    // Shape - superclass

    function Shape() {

      this.x = 0;

      this.y = 0;

    }

    // superclass method

    Shape.prototype.move = function(x, y) {

      this.x += x;

      this.y += y;

      console.info('Shape moved.');

    };

    // Rectangle - subclass

    function Rectangle() {

      Shape.call(this); // call super constructor.

    }

    // subclass extends superclass

    Rectangle.prototype = Object.create(Shape.prototype);

    Rectangle.prototype.constructor = Rectangle;

    var rect = new Rectangle();

    console.log('Is rect an instance of Rectangle?',

      rect instanceof Rectangle); // true

    console.log('Is rect an instance of Shape?',

      rect instanceof Shape); // true

    rect.move(1, 1); // Outputs, 'Shape moved.'

    转自:https://segmentfault.com/a/1190000011461557

    推荐↓↓↓ 

     

    web前端开发热门新闻,每日 10:24 播报

  • 相关阅读:
    Docker磁盘垃圾清理
    什么是容器编排?
    Docker 容器连接
    docker入门操作整理
    Docker学习的几个建议和技巧
    支付清结算之电商侧处理
    在Linux 中进入单用户模式的技巧
    教你如何构建异步服务器和客户端的 Kotlin 框架 Ktor
    NetSuite助力各行业企业快速发展
    linux需要你的不懈努力
  • 原文地址:https://www.cnblogs.com/lcc1995/p/7685362.html
Copyright © 2020-2023  润新知