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的行为。

目录
相关文章
|
9天前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
13天前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
1月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理与实战
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理与实战
|
13天前
|
设计模式 JavaScript 前端开发
js中new和object.creat区别
【10月更文挑战第29天】`new` 关键字和 `Object.create()` 方法在创建对象的方式、原型链继承、属性初始化以及适用场景等方面都存在差异。在实际开发中,需要根据具体的需求和设计模式来选择合适的方法来创建对象。
|
13天前
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
1月前
|
前端开发 JavaScript
深入理解JavaScript中的事件循环(Event Loop):从原理到实践
【10月更文挑战第12天】 深入理解JavaScript中的事件循环(Event Loop):从原理到实践
36 1
|
1月前
|
JavaScript 前端开发 大数据
在JavaScript中,Object.assign()方法或展开语法(...)来合并对象,Object.freeze()方法来冻结对象,防止对象被修改
在JavaScript中,Object.assign()方法或展开语法(...)来合并对象,Object.freeze()方法来冻结对象,防止对象被修改
15 0
|
1月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:原理、应用与代码演示
【10月更文挑战第12天】深入理解JavaScript中的闭包:原理、应用与代码演示
|
4月前
|
存储 JavaScript 前端开发
|
JavaScript
js基础笔记学习247event对象3
js基础笔记学习247event对象3
65 0
js基础笔记学习247event对象3