new fn() 做了什么

1. 函数的多重用途

函数可以搭配new使用,函数内部的this会指向一个新对象,最终函数会返回这个新对象

function fn(){
  this.count = 1
}
const obj = new fn()  // {count: 1}

fn()   // undefined  全局对象上会添加一个属性count,值为 1
复制代码

js函数有两个不同的内部方法:[[Call]][[construct]]

  • 使用new调用函数时,会执行[[construct]]方法:
    1. 在内存中创建一个继承自 fn.prototype 的新对象
    2. 把函数体的this指向新对象,并执行函数体。
    3. 默认返回新对象。如果构造函数指定的返回值为复杂类型,会覆盖掉默认返回值。
  • 直接调用函数,会执行[[Call]]方法,直接执行函数体。

使用代码模拟new的操作:

function New(fn, ...arg) {
  // 创建新对象,原型为构造函数的原型
  let res = Object.create(fn.prototype)
  // 修改this指向为新对象,并执行函数体
  let ret = fn.apply(res, arg)
  // 如果返回值不是有效对象,则返回新对象
  return (typeof ret === 'object' && typeof ret === 'function' && ret !== null) ? ret : res
}
function Fn(a) {
  this.a = a
}
Fn.prototype = {
  sayHello() {
      console.log('hello')
  }
}
console.log(new Fn(2))   
console.log(new(Fn, 2))
复制代码

在控制台查看输出时,唯一不同的是红框的地方: new操作符创造的对象会有一个Fn。并不太清楚这是什么,后来猜测这是 实例对象的构造函数名,于是尝试给 Fn的原型加上了 constructor属性:

Fn.prototype = {
  constructor: Fn,
  sayHello() {
    console.log(1)
  }
}
复制代码

然后输出完全一致了。

那么问题来了:为什么 new操作符创建的实例在原型没有constructor属性时,也能够知道实例的构造函数,而且并没有设置constructor属性,在控制台第一次的输出中可以看到。望有缘人能帮忙解答!!!

2. es5中判断函数被调用的方式

根据上述原理,es5经常依据this是否为构造函数的实例,来判断函数被调用的方式

function Person(){
  if(this instanceof Person){
      alert('使用new,函数被当做构造函数调用')
  }else{
      alert('当做普通函数调用')
  }
}
复制代码

以前经常 使用面向对象开发jquery插件,发现有些插件初始化时,可以使用new也可以不使用new ,比如let swiper = new Swiper(options)let swiper = Swiper(options)均可,观察后发现,插件结构是这样的:

// 忘记 Swiper是否可以不使用new初始化,这里就拿它举例说明了
function Swiper(option){
 if(this instanceof Swiper){
     // 初始化操作
 }else{
     return new Swiper(option)
 }
}
复制代码

把函数的两种调用方式,都过滤为构造函数式调用,所以是否使用new都可以。

题外话: 当时自己也偷学了这种方式,来写插件,觉得很高级,但总是忘记具体写法。学习的高潮之处,就在于新知识与自己的旧知识体系碰撞并融合的过程,最后融为了一声 "奥~~,TM原来如此"

但这种方式也有缺陷因为可以用call或者apply修改函数内的this 指向到函数的实例上,比如let person = new Person(); Person.call(person),那么就不能区分是否通过new调用

3. es6判断函数调用方式

为了判断函数是否通过new调用,es6引入了new.target这个元属性。

元属性:是指非对象的属性,可以提供非对象目标的补充信息

  1. 使用new调用函数时,会执行[[construct]]方法,new.target被赋值为函数本身
  2. 直接调用函数,会执行[[Call]]方法,new.targetundefined
function Person(){
    if(new.target === Person){
        // 使用 new调用
        console.log(new.target)
    }else{
        console.log(new.target)
    }
}
new Person()   // Person
Person()   // undefined
复制代码

new.target在函数体外是一个语法错误

了解更多es6中新增的函数特性

转载于:https://juejin.im/post/5c98f627e51d453b2e0846bd

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值