JS数据类型之Object(二)创建对象

创建对象

1、字面量

let obj = {
  property_i : value_i,
}

2、Object构造函数

let obj = new Object();

大部分情况下,我们都是通过构造函数创建对象。常用的对象有:new String()|new Number()|new Boolean()|new Date()|new RegExp()| new Function()


以上两种方法的缺点:创建具有同样接口的多个对象需要重复多个代码。


3、工厂模式

两步走:
1、创建工厂
2、运行工厂

// 1、创建工厂
function createPerson(name, age, job) {
  let o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function() {
    console.log(this.name)
  }
  return o;
}

// 2、运行工厂
let person = createPerson("张三", 18, "程序员");

console.dir(person)

运行结果:


图片.png

优点:解决创建多个类似对象的问题
缺点:没有解决对象标识问题(即新创建的对象是Object类型,没有具体化)


4、构造函数模式

两步走:
1、通过创建一个构造函数来定义对象的类型,首字母大写是非常普通而且很恰当的惯用法。
2、通过new创建对象实例。
注意事项:
1.使用this关键字
2.首字母大写

// 第一步
function Car(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.driver = function () {
        console.log("driver")
    } // 可以将此函数定义在构造函数的外部,但是不规范【全局作用域被搞乱了】
    // return this; // 会自动return this
}

// 第二步
var car1 = new Car("Nissan", "300ZX", 1992);
var car2 = new Car("Nissan1", "300ZX1", 2000);

console.dir(car1)

console.log(car1.driver == car2.driver)

运行结果:


图片.png
使用new操作符执行的操作
new操作的执行流程.png

1、在内存中创建一个新对象。
2、在这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性
3、构造函数的内部的this被赋值为这个新对象(this指向新对象)
4、执行构造函数内部的代码(给新对象添加属性)
5、如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象

构造函数 VS 函数

构造函数与普通函数唯一的区别就是调用方式不同。


构造函数的优点:解决对象标识问题
构造函数的缺点:构造函数中定义的方法会在每个实例上都创建一遍,各个实例会定义不同的Function实例。


5、原型模式

每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定类型的实例共享的属性和方法。

function Person() {}

Person.prototype.name = "张三";
Person.prototype.age = 30;
Person.prototype.job = "程序员";
Person.prototype.sayName = function() {
    console.log(this.name)
}

let person1 = new Person();
let person2 = new Person();

person1.sayName() // 张三
console.log(person1.sayName == person2.sayName) // true
原型性质
  • 原型的写法

1、单个写
这样写只是在原型上新增属性和方法,已有的属性和方法不会受到影响。

function Person() {}

Person.prototype.name = "张三";
Person.prototype.sayName = function() {
    console.log(this.name)
}

2、合并写
给构造函数的原型重新赋值时,原来的原型将被替换,其中最重要的属性是constructor,constructor指向构造函数,而且还是不可迭代的。[[Enumerable]]的值为false。

function Person() {}

Person.prototype = {
    name: "张三",
    sayName: function() {
        console.log(this.name);
    }
} // 对象重置,原有的属性均消失
Object.defineProperty(Person.prototype, "constructor", {
    enumerable: false,
    value: Person
});
  • 原型的动态性
    任何时候对原型所做的修改会在实例上反映出来。注意不是给原型重新赋值。

  • 原生对象原型
    不要修改原生对象的原型,因为可能造成误会或命名冲突。推荐的做法是创建一个自定义的类型,继承原生对象。


原型模式的缺点:弱化了构造函数传递初始化参数的能力;原型模式的共享特性的弊端,所有实例都会影响共享特性。


构造函数 VS 原型 VS 实例

构造函数:类似与类,与函数类似,只是通过new使用
原型:所有的JavaScript对象至少继承于一个对象,被继承的对象被称为原型。JS中的每个函数都有一个prototype原型属性,这个属性也是一个对象,用途是包括能够由特定类型的全部实例共享的属性和方法。
实例:new 构造函数之后得到的对象。

构造函数、原型、实例关系图.png
  • 只要创建一个函数,就会按照特定的规则为这个函数创建一个prototype属性,指向原型对象,原型是一个实例对象。
  • 默认情况下,所有原型对象自动获取一个名为constructor属性,指回与之关联的构造函数,即循环引用。Person.prototype.constructor = Person
  • 实例化对象中的__proto__属性指向原型,构造函数中的prototype属性指向原型,即person.__proto__ = Person.prototype
  • 原型中的__proto__指向父原型的prototype,即Person.prototype.__proto__ == Object.prototype
  • 正常的原型链都会终止与Object的原型,即Person.prototype.__proto__.constructor == Object
  • Object原型的原型是nullObject.prototype.__proto__ == null; // true
  • instanceof 操作符检查实例的原型链中是否包含指定构造函数的原型。
  • 使用原型的isPrototypeOf()方法确定是否属于这个原型。
  • 获取一个实例的原型Object.getPrototypeOf(实例)
  • 设置一个实例的原型Object.setPrototypeOf(实例, 原型);此方法会严重影响代码性能,可以使用Object.create()方法创建一个指定原型的对象。

通过Object.create()方法创建时,我们可以为创建的对象选定一个原型对象,而不用定义构造函数。

  • 参数:
    proto:新创建对象的原型对象;
    propertiesObject:为新创建的对象添加指定的属性值和对应的属性描述符。
  • 返回值:一个新对象,带着指定的原型对象和属性。
  • 注意事项:
    Object.create(object, propertiesObject)中propertiesObject是null或非原始包装对象,则会抛出TyperError异常。
// 使用对象初始化器创建一个对象
var Animal = {
  type: "Invertebrates", // 属性默认值
  displayType : function() {  // 用于显示type属性的方法
    console.log(this.type);
  }
}

// 通过Object.create()创建对象animal
var animal1 = Object.create(Animal);
animal1.displayType(); // Output:Invertebrates

// 创建一种新的动物——Fishes
var fish = Object.create(Animal);
fish.type = "Fishes";
fish.displayType(); // Output:Fishes
原型层级
  • 访问属性和方法的流程
    通过对象访问属性的时候,根据属性名称从对象自身开始搜索,如果自身有此属性,则返回对应的值;如果自身没有此属性,则到最近原型上搜索,直到原型为null或搜索到此属性。


    访问属性的流程.png
  • 属性的覆盖
    实例是有与原型同名的属性,会将原型上同名的属性给覆盖。
属性的位置
hasOwnProperty() in
作用 判断自身是否有此属性 访问自身或原型上的属性
使用方法 in操作符:判断对象是否有此属性[原型和自身]
for..in:遍历对象上可迭代的属性[原型和自身]
属性枚举

以下方法受到enumerable原型链的影响。

方法 for...in循环 Object.keys(o) Object.getOwnPropertyNames(o) Object.getOwnPropertySymbols(o)
含义 返回对象及原型链上可枚举的属性 返回自身的所有可枚举属性 返回自身所有属性 返回自身所有的Symbols属性
是否可枚举 不关心 Symbol
是否访问原型链 访问 不访问 不访问 不访问
是否访问Symbol 不访问 不访问 不访问 访问
  • Object.getOwnPropertyNames()
    作用:返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
  • Object.getOwnPropertySymbols()
    作用:返回一个给定对象自身的所有 Symbol 属性的数组。

6、使用Object.assign方法

Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。
Object.assig(target, ...sources)

  • 参数:
    target:目标对象
    sources:源对象

  • 返回值:
    目标对象

  • 过程:
    Object.assign方法只会拷贝源对象自身并可枚举的属性到目标对象。该方法会使用源对象的getter和setter方法。如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面源对象的属性。

  • 注意事项
    1、Object.assign()`是一个浅拷贝。
    2、继承属性和不可枚举属性是不能拷贝的
    3、Object.assgin()方法没有创建原型。

  • 使用场景

    1. 合并对象:多个对象进行合并。
    2. 复制对象:对象的浅拷贝。

对象的迭代

枚举一个对象的属性

以下方法受到enumerable原型链的影响。

方法 for...in循环 Object.keys(o) Object.getOwnPropertyNames(o) Object.getOwnPropertySymbols(o)
含义 返回对象及原型链上可枚举的属性 返回自身的所有可枚举属性 返回自身所有属性 返回自身所有的Symbols属性
是否可枚举 不关心 Symbol
是否访问原型链 访问 不访问 不访问 不访问
是否访问Symbol 不访问 不访问 不访问 访问
for...in

作用:以任意顺序遍历一个对象的除Symbol以外的可枚举属性【包含原型链上可枚举的属性】

Object.keys()

作用:返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。

对象迭代

ES2017新增两个静态方法Object.values()、Object.entries(),用于将对象内容转换为序列化的、可迭代的的格式。

Object.values(Object)

作用:返回一个给定对象自身的所有可枚举属性值的数组。

Object.entries(Object)

作用:返回一个给定对象自身可枚举属性的键值对数组

对象的继承

所有的JavScript对象至少继承于一个对象,被继承的对象被称为原型。
每个对象可以通过构造函数的prototype属性找到原型 或 每个实例对象有一个私有属性__proto__指向原型

获取对象原型

方法一:Object.getPrototypeOf()方法返回指定对象的原型。
方法二:实例化对象.__proto__

Object.getPrototypeOf(object)

  • 参数
    obj:要返回其原型的对象
  • 返回值
    给定对象的原型,如果没有继承属性,则返回null

设置或修改对象原型

方法一:Object.create()创建对象的时候指定原型.
方法二:Object.prototype.__proto__
方法三:Objcet.setPrototypeOf()
方法四:Reflect.setPrototypeOf()

Object.create(proto, propertiesObject):

  • 参数:
    proto:新创建对象的原型对象
    propertiesObject:可选
  • 返回值
    一个新对象,带着指定的原型对象的属性。
<!DOCTYPE html>
<html>
   <head>
       <meta charset="utf-8">
   </head>
   <body>

       <script type="text/javascript">
           "use strict"
           // Shape - 父类(superclass)
           function Shape() {
             this.x = 0;
             this.y = 0;
           }
           
           // 父类的方法
           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.
           }
           
           // 子类续承父类,以下两句话一般同时存在
           Rectangle.prototype = Object.create(Shape.prototype);
           Rectangle.prototype.constructor = Rectangle;
           
           var rect = new Rectangle();
           
           console.dir(rect) // rect --> Rectangle---->Shape---->Object
       </script>
   </body>
</html>

Objcet.setPrototypeOf(obj, prototype)

  • 参数:
    obj:设置其原型对象
    prototype:对象的新原型(对象或null)
  • 过程:
    如果对象的原型被修改成不可扩展(通过Object.isExtensible())的,则抛出TypeError异常。
    如果prototype新原型参数不是对象或努力了,则什么也不做。
    否则,该方法修改对象的原型。

Reflect.setPrototypeOf(target, prototype)

  • 参数:
    target:设置其原型对象
    prototype:对象的新原型(对象或null)
  • 返回值:
    返回一个boolean类型表明是否原型已经设置成功。
  • 过程:
    如果参数target不是Object,或者prototype既不是对象,也不是null,则抛出TypeError异常。

Object原型与属性相关的方法

方法 作用 参数 返回值
obj.hasOwnProperty() 判断属性是否在某个对象自身上 {不含原型链} prop:要检测的属性 true:有
false:无
prop in obj 判断属性是否在某个对象上 {含原型链} prop:要检测的属性 true:有
false:无
prototypeObj.isPrototypeOf(obj) 测试一个对象是否在另一个对象的原型链上 object:在该对象的原型链上搜寻 true:在
false:不
instanceof操作符 检测构造函数的prototype属性是否会出现在某个实例对象的原型链上 操作符,不是函数 true:在
false:不在
obj.propertyIsEnumerable(prop) 判断属性名是否可枚举 prop需要测试的属性 true:枚举
false:非枚举
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容