JavaScript 自定义对象 及 new()原理与实现 如何完整地手写实现new

简介: JavaScript 自定义对象 及 new()原理与实现 如何完整地手写实现new

JavaScript 自定义对象

new() 原理与实现


object 表示类型,是 JavaScript 的六种 主要类型 之一(ES6后新增了Symbol为7种) ,也就是我们所说的 “对象” ,用于存储各种键值集合和更复杂的实体。Object()(首字母大写)是JavaScript语言中提供的用于创建 object 的构造器,相当于一些语言中的 Object 类,也就是说在 JavaScript 中所有的对象都是 Object "类"的实例。

使用new是运算符用于创建对象的方式之一。

1. JavaScript中自定义对象的方法

1.1 直接创建

在一对花括号中填写若干个属性名和属性值的方法可以用于直接创建对象,这是最简单也最直观的方法,其格式为:

var objName = {
    attribName1: attribvalue1, 
    attribName2: attribvalue2, 
    ...
  }

例如:

var student = {
  name: '李华',
  age: 12,
  sex: 'male'
}

1.2 通过 Object 对象创建

Object是JavaScript内置对象构造器,

obj = new Object([value])

例如:

var student = new Object();
student.name = '李华';
student.age = 12;
student.sex = 'male';

这与 1.1 节例子中创建的对象是一样的。

1.3 通过自定义构造函数创建

以下这种方式只能在非严格模式下成立:

// 1. 定义作为`构造函数`的函数
function Teacher(name, sex, height){
  this.name = name;
  this.sex = sex;
  this.height = height;
}
// 创建对象实例
var teacher1 = new Teacher("ZhangSan", "male", 190);

1.4 关于 this 的指向的说明

1. 在全局执行环境中(在任何函数体外部)this 都指向全局对象。在浏览器中, window 对象同时也是全局对象。

【注意】在严格模式下将有所不同,函数内部的this会若无赋值,将一直保持为undefined

2. 在函数内部,this的值取决于函数被调用的方式

1.3 节中,如果直接调用Teacher函数即执行("ZhangSan", "male", 190)this将指向window对象(非严格模式)。然而将其作为构造器使用时,即执行new Teacher("ZhangSan", "male", 190)时,new运算符改变了this的指向,使其不再指向window,而是指向新创建的对象。这在之后的自己实现 new章节的代码中将会看到具体的过程。

2. new 运算符在创建自定义对象的过程

new运算符创建一个用户定义的对象类型的实例具有构造函数的内置对象的实例。具体来说,它做了以下工作:

  • (1)创建一个空的简单JavaScript对象(即{});
  • (2)为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象
  • (3)将(1)中新创建的对象作为this的上下文 ;
  • (4)如果该函数没有返回对象,则返回this

Object.prototype.__proto__已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。通过现代浏览器的操作属性的便利性,可以改变一个对象的 [[Prototype]] 属性, 这种行为在每一个JavaScript引擎和浏览器中都是一个非常慢且影响性能的操作,使用这种方式来改变和继承属性是对性能影响非常严重的,并且性能消耗的时间也不是简单的花费在 obj.proto = … 语句上, 它还会影响到所有继承来自该 [[Prototype]] 的对象,如果你关心性能,你就不应该在一个对象中修改它的 [[Prototype]]。

3. 自己实现一个拥有new功能的函数

3.1 完整手写实现

为了完整体现new的过程,这里禁止调用Object.create()Function.prototype.apply()这两个方法,完整地按照2 小节中的步骤进行实现。

function myNew(cstrct,...params){
  // 1.创建一个空的JavaScript对象 obj
  const obj = {};
  // 2.为新创建的对象(obj)添加属性`__proto__`,
  // 并将该属性 链接至 构造函数的 原型对象  ;
  obj.__proto__ = cstrct.prototype; 
  // 3. 将该新对象(obj)作为this的上下文 ;
  // 3.1 在 obj.__proto__ 上临时挂上构造函数
  obj.__proto__._func = cstrct;
  // 3.2 执行该构造函数得到 "this"
  let _ = obj._func(...params);
  // 3.3 删除该临时挂载的属性 _func(否则实例上会多出这个属性)
  delete obj.__proto__._func
  // 4. 如果该函数没有返回对象,即null或undefined,
  // 则返回this,否则返回该对象
  return _ instanceof Object ? _ : obj;
}

3.2 调用现成方法实现

了解了完整地实现过程之后,再介绍两个方法:

方法 语法 描述
Object.create() Object.create(proto,[propertiesObject]) 使用指定的原型对象和属性创建一个新对象。
Function.prototype.apply() func.apply(thisArg, [argsArray]) 用于调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
返回 调用有指定this值和参数的函数的结果。

因此3.1 节中的

function myNew(cstrct,...params){
  const obj = Object.create(cstrct.prototype)
  obj.__proto__._func = cstrct;
  // 3.2 执行该构造函数得到
  let _ = obj._func(...params);
  // 删除该临时挂载的属性 _func(否则实例上会多出这个属性)
  delete obj.__proto__._func
  return _ instanceof Object ? _ : obj;
}

4. 使用 new 创建 类(class) 实例

首先很遗憾地说,JavaScript 并不是基于 的面向对象编程语言,即使在有了class关键字后,本质上还是基于原型链的,而class只是为了模仿其它语言中的类而新增的语法糖。ES6引入了也就是class,其本质上是一个JavaScript 函数

例如:

class Car{
  public color;
  public height;
  constructor(color,height){
    this.color = color;
    this.height = height;
  }
  run(){
    console.log('I am running at high speed...');
  }
  serfIntroduce(){
    console.log(`I am a ${this.color} car with a height of ${this.height.toString()} meters.`)
  }
}
const lamborghini = new Car('green',1.05)

实际上上面的代码只是以下代码使用class语法糖的结果:

var Car = (function () {
    function Car(color, height) {
        this.color = color;
        this.height = height;
    }
    Car.prototype.run = function () {
        console.log('I am running at high speed...');
    };
    Car.prototype.serfIntroduce = function () {
        console.log("I am a ".concat(this.color, " car with a height of ").concat(this.height.toString(), " meters."));
    };
    return Car;
}());
var lamborghini = new Car('green', 1.05);
lamborghini.serfIntroduce();
lamborghini.run();

从以上代码可以看出虽然自从ES6标准发布后在形式上看JavaScript中的"类"看起来就像 Java 等语言中的类用法一样,但本质上JavaScript中的new只是模拟了Java等语言中new的行为。

目录
相关文章
|
6天前
|
存储 JavaScript 索引
js开发:请解释什么是ES6的Map和Set,以及它们与普通对象和数组的区别。
ES6引入了Map和Set数据结构。Map的键可以是任意类型且有序,与对象的字符串或符号键不同;Set存储唯一值,无重复。两者皆可迭代,支持for...of循环。Map有get、set、has、delete等方法,Set有add、delete、has方法。示例展示了Map和Set的基本操作。
18 3
|
1天前
|
JavaScript 前端开发
JavaScript DOM 文档对象模型(获取、改变html元素)
JavaScript DOM 文档对象模型(获取、改变html元素)
|
5天前
|
前端开发 JavaScript
前端 富文本编辑器原理——从javascript、html、css开始入门(二)
前端 富文本编辑器原理——从javascript、html、css开始入门
17 0
前端 富文本编辑器原理——从javascript、html、css开始入门(二)
|
5天前
|
前端开发 JavaScript 索引
前端 富文本编辑器原理——从javascript、html、css开始入门(一)
前端 富文本编辑器原理——从javascript、html、css开始入门
16 0
|
8天前
|
JavaScript 前端开发 开发者
JavaScript中的错误处理:try-catch语句与错误对象
【4月更文挑战第22天】JavaScript中的错误处理通过try-catch语句和错误对象实现。try块包含可能抛出异常的代码,catch块捕获并处理错误,finally块则无论是否出错都会执行。错误对象提供关于错误的详细信息,如类型、消息和堆栈。常见的错误类型包括RangeError、ReferenceError等。最佳实践包括及时捕获错误、提供有用信息、不忽略错误、利用堆栈信息和避免在finally块中抛错。
|
8天前
|
前端开发 JavaScript 编译器
深入解析JavaScript中的异步编程:Promises与async/await的使用与原理
【4月更文挑战第22天】本文深入解析JavaScript异步编程,重点讨论Promises和async/await。Promises用于管理异步操作,有pending、fulfilled和rejected三种状态。通过.then()和.catch()处理结果,但可能导致回调地狱。async/await是ES2017的语法糖,使异步编程更直观,类似同步代码,通过事件循环和微任务队列实现。两者各有优势,适用于不同场景,能有效提升代码可读性和维护性。
|
12天前
|
JavaScript 前端开发
JavaScript BOM 浏览器对象模型
JavaScript BOM 浏览器对象模型
|
12天前
|
JavaScript 前端开发 API
JavaScript DOM 文档对象模型
JavaScript DOM 文档对象模型
|
8月前
|
JSON JavaScript 前端开发
JavaScript的自定义对象
JavaScript的自定义对象
|
12月前
|
存储 JavaScript 前端开发
JavaScript 自定义对象 及 new原理与实现 如何完整地手写实现new
JavaScript 自定义对象 及 new 原理与实现 如何完整地手写实现new
137 0
JavaScript 自定义对象 及 new原理与实现 如何完整地手写实现new