当前位置: 首页 > news >正文

JS红宝书笔记 8.2 创建对象

虽然使用Object构造函数或对象字面量可以方便地创建对象,但这些方式有明显不足:创建具有同样接口的多个对象需要重复编写很多代码

工厂模式可以用不同的参数多次调用函数,每次都会返回一个新对象,这种模式虽然可以解决创建多个类似对象的问题,但没有解决对象表示问题,即新创建的对象是什么类型

构造函数和工厂函数的区别:

  • 没有显式的创建对象
  • 属性和方法字节赋值给了this
  • 没有return

使用new操作符调用构造函数会执行如下操作:

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

constructor本来是用于标识对象类型的,不过,一般认为instanceof操作符是确定对象类型更可靠的方式

相比于工厂模式,定义自定义构造函数可以确保实例被标识为特定类型。

构造函数不一定要写成函数声明的形式,赋值给变量的函数表达式也可以表示构造函数,在实例化时,如果不想传参数,那么构造函数后面的括号可加可不加。只要有new操作符,就可以调用相应的构造函数

构造函数与普通函数唯一的区别就是调用方式不同,除此之外,构造函数也是函数,并没有把某个函数定义为构造函数的特殊语法。任何函数只要使用new操作符调用就是构造函数,而不是用new操作符调用的函数就是普通函数。

构造函数的主要问题在于,其定义的方法会在每个实例上都创建一遍,因为都是做一样的事,所以没必要定义两个不同的Function实例,况且,this对象可以把函数与对象的绑定推迟到运行时


每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实例上,这个对象就是通过调用构造函数创建的对象的原型,使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型

无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个prototype属性指向原型对象,默认情况下,所有原型对象自动获得一个名为constructor的属性,指回与之关联的构造函数。

在自定义构造函数时,原型对象默认只会获得constructor属性,其他的所有方法都继承自Object,每次调用构造函数创建一个新实例,这个实例的内部[[Prototype]]指针就会被赋值为构造函数的原型对象。脚本中没有访问这个[[Prototype]]的标准方式,一些浏览器会在每个对象上暴露__proto__属性,通过这个属性可以访问对象的原型。

实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有

虽然不是所有实现都对外暴露了[[Prototype]],但可以使用isPrototypeOf()方法确定两个对象之间的关系,isPrototypeOf()会在传入参数的[[Prototype]]指向调用它的对象时返回true

Object有一个方法叫Object.getPrototype(),返回参数的内部特性[[Prototype]]的值

Object.setPrototypeOf()可以向实例的私有特性[[Prototype]]写入一个新值,这样就可以重写一个对象的原型继承关系,但是会影响代码性能

Object.create()可以创建一个新对象,同时为其指定原型

原型层级

在通过对象访问属性时,会按照这个属性的名称开始搜索,搜索开始于对象实例本身,如果在这个实例上发现了给定的名称,则返回该名称对应的值,如果没有找到这个属性,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值,这就是原型用于在多个对象实例间共享属性和方法的原理

虽然可以通过实例读取原型对象上的值,但不能通过实例重写这些值,如果在实例上添加了一个原型对象中同名的属性,那就会在实例上创建这个属性,这个属性会遮住原型对象上的属性。

使用delete操作符可以删除实例上的属性

hasOwnProperty()方法用于确定某个属性是在实例上还是在原型对象上,这个方法是继承自Object的,会在属性存在于调用它的对象实例上时返回true

原型和in操作符

有两种方式使用in操作符:单独使用和在for-in循环中使用

在单独使用时,in操作符会在可以通过对象访问指定属性时返回true,无论该属性在实例上还是原型上。

如果要确定某个属性是否在原型上,可以同时使用hasOwnProperty()和in操作符

在for-in循环中使用in操作符时,可以通过对象访问且可以被枚举的属性都会返回,包括实例和原型属性

要获得对象上所有可枚举的实例属性,可以使用Object.keys()方法,这个方法接收一个对象作为参数,返回包含该对象所有可枚举属性名称的字符串数组

如果想列出所有实例属性,无论是否可以枚举,都可以使用Object.getOwnPropertyNames()

Object.getOwnPropertySymbols()方法针对符号

属性枚举顺序

for-in循环和Object.keys()的枚举顺序是不确定的

Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()和Object.assign()的枚举顺序是确定的,先以升序枚举数值键,然后以插入顺序枚举字符串和符号键,在对象字面量中定义的键以它们逗号分隔的顺序插入


Object.values()和Object.entries()将对象内容转换为序列化的,可迭代的格式,它们接收一个对象,返回它们内容的数组。Object.values()返回对象值的数组,Object.entries()返回键值对的数组,非字符串属性会被转换为字符串输出,这两个方法执行对象的浅复制,符号属性会被忽略

原型上搜索值是动态的,所以即使实例在修改原型之前已经存在,任何时候对原型对象的修改也会在实例上反映出来

实例的[[Prototype]]指针是在调用构造函数时自动赋值的,这个指针即使把原型修改为不同的对象也不会变,重写整个原型会切断最初原型与构造函数的联系,但实例引用的仍然是最初的原型

重写构造函数上的原型之后再创建的实例才会引用新的原型

通过原生对象的原型可以取得所有默认方法的引用,也可以给原生类型的实例定义新的方法。可以像修改自定义对象原型一样修改原生对象原型,因此随时可以添加方法

原型模式弱化了向构造函数传递初始化参数的能力,会导致所有实例默认都取得相同的属性值,原型的最主要问题是它的共享特性

相关文章:

  • Mybatis之Integer类型字段为0,入库为null
  • Spring-创建第一个SpringBoot项目
  • html实现登录与注册功能案例(不写死且只使用js)
  • Ubuntu编译ffmpeg解决错误:ERROR: avisynth/avisynth_c.h not found
  • Kafka性能压测报告撰写
  • Vue3中使用 Vue Flow 流程图方法
  • 103. 2017年蓝桥杯省赛 - 日期问题(困难)- 暴力枚举
  • (哈希)128. 最长连续序列
  • 华为ModelArts详解
  • 使用 mysql2/promise 模块返回以后,使用 await 返回数据总结
  • 时序数据库概念及IoTDB特性详解
  • C++位图
  • FPGA基础 -- Verilog 命名事件
  • Debian配置Redis主从、哨兵
  • Rsync+sersync实现数据实时同步(小白的“升级打怪”成长之路)
  • C++实现异步(重叠)管道通信
  • GameFormer论文阅读
  • 46道Jenkins高频题整理(附答案背诵版)
  • 什么是Nacos?
  • Docker+Jenkins+git实现Golang项目自动部署
  • 群晖nas安装wordpress安装/东莞网站建设优化诊断
  • 政府网站建设方面存在的问题/常用的网络推广方法有
  • 尼乐清网站建设/微信营销怎么做
  • 培训网站源码wordpress/制作电商网站
  • 做网站要多长时间/广州专门做seo的公司
  • 门户网站建设为企业带来的好处/电商代运营