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

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

JavaScript 自定义对象 及 new 原理与实现


李俊才(jcLee95)的个人博客

已入驻阿里云社区

邮箱 :291148484@163.com
本文地址

目 录


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

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

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

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


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

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

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

1.1 直接创建

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

varobjName= {
attribName1: attribvalue1, 
attribName2: attribvalue2, 
...  }

例如:

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

1.2 通过 Object 对象创建

Object是JavaScript内置对象构造器,

obj=newObject([value])

例如:

varstudent=newObject();
student.name='李华';
student.age=12;
student.sex='male';

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

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

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

// 1. 定义作为`构造函数`的函数functionTeacher(name, sex, height){
this.name=name;
this.sex=sex;
this.height=height;
}
// 创建对象实例varteacher1=newTeacher("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 小节中的步骤进行实现。

functionmyNew(cstrct,...params){
// 1.创建一个空的JavaScript对象 objconstobj= {};
// 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(否则实例上会多出这个属性)deleteobj.__proto__._func// 4. 如果该函数没有返回对象,即null或undefined,// 则返回this,否则返回该对象return_instanceofObject?_ : obj;
}

3.2 调用现成方法实现

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

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

因此3.1 节中的

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

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

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

例如:

classCar{
publiccolor;
publicheight;
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.`)
  }
}
constlamborghini=newCar('green',1.05)

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

varCar= (function () {
functionCar(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."));
    };
returnCar;
}());
varlamborghini=newCar('green', 1.05);
lamborghini.serfIntroduce();
lamborghini.run();

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

目录
相关文章
|
1月前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
1月前
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
1月前
|
JSON 前端开发 JavaScript
JavaScript中对象的数据拷贝
本文介绍了JavaScript中对象数据拷贝的问题及解决方案。作者首先解释了对象赋值时地址共享导致的值同步变化现象,随后提供了五种解决方法:手动复制、`Object.assign`、扩展运算符、`JSON.stringify`与`JSON.parse`组合以及自定义深拷贝函数。每种方法都有其适用场景和局限性,文章最后鼓励读者关注作者以获取更多前端知识分享。
19 1
JavaScript中对象的数据拷贝
|
1月前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
73 1
|
1月前
|
监控 JavaScript 算法
深度剖析 Vue.js 响应式原理:从数据劫持到视图更新的全流程详解
本文深入解析Vue.js的响应式机制,从数据劫持到视图更新的全过程,详细讲解了其实现原理和运作流程。
|
1月前
|
设计模式 JavaScript 前端开发
js中new和object.creat区别
【10月更文挑战第29天】`new` 关键字和 `Object.create()` 方法在创建对象的方式、原型链继承、属性初始化以及适用场景等方面都存在差异。在实际开发中,需要根据具体的需求和设计模式来选择合适的方法来创建对象。
|
1月前
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
1月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
57 0
|
1月前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
26 1
JavaScript中的原型 保姆级文章一文搞懂