这篇文章主要介绍了关于JS中new调用函数的原理介绍,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

JavaScript 中经常使用构造函数创建对象(通过 new 操作符调用一个函数),那在使用 new 调用一个函数的时候到底发生了什么?先看几个例子,再解释背后发生了什么。

1)看三个例子

1.1 无 return 语句

构造函数最后没有 return 语句,这也是使用构造函数时默认情况,最后会返回一个新对象,如下:function Foo(age) {

this.age = age;

}

var o = new Foo(111);

console.log(o);

这是常见的使用构造函数创建对象的过程,打印出来的是 {age: 111}。

1.2 return 对象类型数据

构造函数最后 return 对象类型数据:function Foo(age) {

this.age = age;

return { type: "我是显式返回的" };

}

var o = new Foo(222);

console.log(o);

打印出来的是 {type: '我是显式返回的'},也就是说,return 之前的工作都白做了,最后返回 return 后面的对象。

1.3 return 基本类型数据

那是不是只要构造函数体内最后有 return,返回都是 return 后面的数据呢?

我们看下返回基本类型数据的情况:function Foo(age) {

this.age = age;

return 1;

}

var o = new Foo(333);

console.log(o);

打印出来的是 {age: 333},和没有 return 时效果一样。跟预期不一样,背后你原理看下面分析。

2)背后原理

2.1 非箭头函数的情况

当使用 new 操作符创建对象是,ES5 官方文档在 函数定义 一节中做了如下定义 13.2.2 [[Construct]]:

When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:Let obj be a newly created native ECMAScript object.

Set all the internal methods of obj as specified in 8.12.

Set the [[Class]] internal property of obj to Object.

Set the [[Extensible]] internal property of obj to true.

Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".

If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.

If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.

Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.

If Type(result) is Object then return result.

Return obj.

看第 8、9 步:8)调用函数 F,将其返回值赋给 result;其中,F 执行时的实参为传递给 [[Construct]](即 F 本身) 的参数,F 内部 this 指向 obj;

9)如果 result 是 Object 类型,返回 result;

这也就解释了如果构造函数显式返回对象类型,则直接返回这个对象,而不是返回最开始创建的对象。

最后在看第 10 步:10)如果 F 返回的不是对象类型(第 9 步不成立),则返回创建的对象 obj。

如果构造函数没有显式返回对象类型(显式返回基本数据类型或者直接不返回),则返回最开始创建的对象。

2.2 箭头函数的情况

那如果构造函数是箭头函数怎么办?

箭头函数中没有 [[Construct]] 方法,不能使用 new 调用,会报错。

NOTICE:其中 [[Construct]] 就是指构造函数本身。相关规范在 ES6 的官方文档 中有提,但自从 ES6 以来的官方文档巨难懂,在此不做表述。

3)new 调用函数完整过程

3.1 中文描述及相关代码分析

除了箭头函数之外的任何函数,都可以使用 new 进行调用,背后发生了什么,上节英文讲述的很清楚了,再用中文描述如下:

1)创建 ECMAScript 原生对象 obj;

2)给 obj 设置原生对象的内部属性;(和原型属性不同,内部属性表示为 [[PropertyName]],两个方括号包裹属性名,并且属性名大写,比如常见 [[Prototype]]、[[Constructor]])

3)设置 obj 的内部属性 [[Class]] 为 Object;

4)设置 obj 的内部属性 [[Extensible]] 为 true;

5)将 proto 的值设置为 F 的 prototype 属性值;

6)如果 proto 是对象类型,则设置 obj 的内部属性 [[Prototype]] 值为 proto;(进行原型链关联,实现继承的关键)

7)如果 proto 是不对象类型,则设置 obj 的内部属性 [[Prototype]] 值为内建构造函数 Object 的 prototype 值;(函数 prototype 属性可以被改写,如果改成非对象类型,obj 的 [[Prototype]] 就指向 Object 的原型对象)

8)9)10)见上节分析。(决定返回什么)

对于第 7 步的情况,见下面代码:function Foo(name) {

this.name = name;

}

var o1 = new Foo("xiaoming");

console.log(o1.__proto__ === Foo.prototype); // true

// 重写构造函数原型属性为非对象类型,实例内部 [[Prototype]] 属性指向 Object 原型对象

// 因为实例是一个对象类型的数据,默认会继承内建对象的原型,

// 如果构造函数的原型不满足形成原型链的要求,那就跳过直接和内建对象原型关联

Foo.prototype = 1;

var o2 = new Foo("xiaohong");

console.log(o2.__proto__ === Foo.prototype); // false

console.log(o2.__proto__ === Object.prototype); // true

3.2 更简洁的语言描述

若执行 new Foo(),过程如下:

1)创建新对象 o;

2)给新对象的内部属性赋值,关键是给[[Prototype]]属性赋值,构造原型链(如果构造函数的原型是 Object 类型,则指向构造函数的原型;不然指向 Object 对象的原型);

3)执行函数 Foo,执行过程中内部 this 指向新创建的对象 o;

4)如果 Foo 内部显式返回对象类型数据,则,返回该数据,执行结束;不然返回新创建的对象 o。

4)几点说明

4.1 判断是否是 Object 类型

关于一个数据是否是 Object 类型,可以通过 instanceof 操作符进行判断:如果 x instanceof Object 返回 true,则 x 为 Object 类型。

由上可知,null instanceof Object 返回 false,所以 null 不是 Object 类型,尽管typeof null 返回 "Object"。

4.2 instanceof 原理

instanceof 的工作原理是:在表达式 x instanceof Foo 中,如果 Foo 的原型(即 Foo.prototype)出现在 x 的原型链中,则返回 true,不然,返回 false。

因为函数的原型可以被改写,所以会出现在 x 通过 Foo new 出来之后完全改写 Foo 的原型 x instanceof Foo 返回 false 的情况。因为实例创建之后重写构造函数原型,实例指向的原型已经不是构造函数的新的原型了,见下面代码:const Foo = function() {};

const o = new Foo();

o instanceof Foo; // true

// 重写 Foo 原型

Foo.prototype = {};

o instanceof Foo; // false

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

php new对象 调用函数,关于JS中new调用函数的原理介绍相关推荐

  1. js中同名的函数的调用情况

    转自:https://www.cnblogs.com/wshiqtb/p/3480597.html html中如果出现函数同名时: 如果有多个外部引入的js文件,例如a.js和b.js(引入顺序假定是 ...

  2. 关于js中的eval函数

    关于JS中的eval()函数 eval()函数的定义和用法 JavaScript中的 eval() 函数可以计算eval中的参数(JavaScript 字符串),并把它作为脚本代码来执行.       ...

  3. js中的offset函数和position

    js中的offset函数,其本身是个对象,它含有两个属性,top和left,其属性值是数字,无单位,是针对文档内容的,即浏览器 而position函数,也有上述两个属性,但它是针对父元素的偏移量 转载 ...

  4. Vue.js 中的渲染函数是什么?如何使用渲染函数?

    Vue.js 中的渲染函数是什么?如何使用渲染函数? Vue.js 是一款流行的前端框架,它提供了许多方便的工具和 API,用于构建交互式的用户界面.其中,渲染函数是 Vue.js 中一个强大的工具, ...

  5. js中的concat函数-字符串拼接+数组拼接

    js中的concat函数-字符串拼接+数组拼接 一.concat() 方法用于字符串拼接字符串 let str1 = "abc"let str2 = "123" ...

  6. JS中自动执行函数小结

    JS中自动执行函数小结 请看以下两个函数: 1.function a(){ 2.    alert("a") 3.} 1.var b= function(){ 2.    aler ...

  7. underscore.js中的节流函数debounce及trottle

    函数节流   throttle and debounce的相关总结及想法 一开始函数节流的使用场景是:放止一个按钮多次点击多次触发一个功能函数,所以做了一个clearTimeout setTimeou ...

  8. js中递归调用返回值为undefined问题

    js中递归调用返回值为undefined问题 问题重现 下面这个简单的递归函数 理论上弹出的是11 但是............. 问题解决 在函数调用处加return

  9. js中写一个函数,第一秒打印1,第二秒打印2

    js中写一个函数,第一秒打印1,第二秒打印2 1.用let块级作用域 for(let i = 0;i<5;i++){setTimeout(()=>{console.log(i);},100 ...

最新文章

  1. python实现迭代的快速排序(Iterative Quick Sort)
  2. CORS跨域实现思路及相关解决方案
  3. 做面板数据分位数回归模型_面板数据门限回归模型
  4. MIT自适应律MRAC的理解和MATLAB实现
  5. suse linux 忘记root密码,SUSE linux 忘记root密码
  6. [模拟] Jzoj P2499 东风谷早苗
  7. 人工智能培训学校学哪些内容
  8. 前端更新需要清空浏览器缓存_js清除浏览器缓存的几种方法
  9. 不用软件批量缩小图片文件的大小
  10. 广工学生“纯手工”打造赛车 将出征F1赛道
  11. wifi 中间人攻击_公共wifi下的中间人攻击
  12. 红外热成像的空间分辨率怎么算?
  13. java xmpp协议_GitHub - zhengzhi530/xmpp: 基于Xmpp协议的即时通讯社交软件(客户端+服务端)...
  14. TFTP协议详细分析
  15. java-php-python-旅游景区预约管理系统计算机毕业设计
  16. 【兄弟反目成仇系列】:我只是写了一个爆炸信息窗口脚本,好兄弟追了我几条街~
  17. 移动电源/便携式汽车应急启动电源UKCA/CE认证
  18. robots文件的Crawl-delay是什么意思
  19. win10 mrt 找不到
  20. 功率二极管的损耗分析和选型原则

热门文章

  1. ppt学习(1) ppt学习网站
  2. 关于textArea的一些琐事
  3. vue 声明周期函数_【Vue】详解Vue生命周期
  4. asp.net mvc5编程实战_深圳荷坳车铣复合编程培训cnc数控编程技术
  5. SAP一句话入门:Human Resource
  6. Debug时如何跳过(不执行)某些代码
  7. 实施SAP:资源和进度
  8. ABAP:ALV中自定义搜索帮助
  9. 上市近一年,良品铺子的“高端化”之路走通了吗?
  10. android log耗性能吗,一个高性能的Android日志库