# New 操作符
Table of Contents
关于 New 操作符的一些东西
语法
new constructor[([arguments])]
- constructor 构造函数,指定对象实例类型的类或者函数
- arguments 参数,被构造函数调用的参数列表
描述
new 关键字会进行如下的操作:
- 创建一个新的对象(即
{})。 - 连接新对象的原形链到构造函数。
- 将构造函数中的
this绑定到新对象上。 - 如果构造函数返回对象,返回该对象,否则返回创建的新对象。
模拟实现:
function _new(fn, ...args) { // 创建空对象,并将原型链绑定到构造对象 // 相当于 obj.__proto__ = fn.prototype let obj = Object.create(fn.prototype)
// 执行构造函数方法,绑定 this let res = fn.apply(obj, args)
// 如果构造函数返回对象,返回该对象,否则返回创建的新对象。 return res instanceof Object ? res : obj}
function Person(name, age) { this.name = name this.age = age}
const p = _new(Person, 'chen', 18)注意
一个函数作为构造函数时候:
- 函数首字母大写(非必须条件)
- 用
new调用调用函数
function T(name) { this.name = name}
var t1 = T('t1')var t2 = new T('t2')console.log(t1) // => "undefined"console.log(t2) // => { name: "t2" }实际上, T 和其他函数没有任何区别。函数本身并不是构造函数,然而,当你在普通的函数调用前面加上 new 关键字之后,就会把这个函数调用变成一个“构造函数调用”。实际上,new 会劫持所有普通函数并用构造对象的形式来调用它。
function NothingSpecial() { console.log('Hello, World.')}
var a = new NothingSpecial()// Hello, World.
a // {}NothingSpecial 只是一个普通的函数,但是使用 new 调用时,它就会构造一个对象并赋值 a。
返回值
在传统语言中,构造函数不应该有返回值,实际执行的返回值就是此构造函数的实例化对象。
如果返回的类型是 object,那么构造函数返回这个 object
function T(name) { this.name = name return { foo: 'foo', }}var t1 = new T('bar')console.log(t1) // { foo: "foo" }如果返回的类型不是 object,那么构造函数返回的是 this
function T(name) { this.name = name}var t2 = new T('bar')console.log(t2) // { name: 'bar' }构造函数
String 构造函数
typeof String('Hello world') // stringtypeof new String('Hello world') // objectNumber 构造函数
const a = new Number('123') // a === 123 is falseconst b = Number('123') // b === 123 is truea instanceof Number // is trueb instanceof Number // is falseBoolean 构造函数
创建的 Boolean 对象默认值为 false
var bNoParam = new Boolean()var bZero = new Boolean(0)var bNull = new Boolean(null)var bEmptyString = new Boolean('')var bfalse = new Boolean(false)var bNaN = new Boolean(NaN)在 JavaScript 中,false、null、0、""、undefined 和 NaN 被称为假值。
创建的 Boolean 对象默认值为 true
var btrue = new Boolean(true)var btrueString = new Boolean('true')var bfalseString = new Boolean('false')var bSuLin = new Boolean('Su Lin')var bArrayProto = new Boolean([])var bObjProto = new Boolean({})题目
function Foo() { getName = function () { console.log(1) } return this}Foo.getName = function () { console.log(2)}Foo.prototype.getName = function () { console.log(3)}var getName = function () { console.log(4)}function getName() { console.log(5)}
//请写出以下输出结果:Foo.getName()getName()Foo().getName()getName()new Foo.getName()new Foo().getName()new new Foo().getName()-
Foo.getName()访问的是 Foo 函数对象储存的静态属性,结果为 2。 -
直接调用
getName(),直接调用就是访问当前上下文作用内的getName函数。由于题目中会有变量提升,所以输出不是 5。题目中最后两行代码的执行顺序如下,所以最终结果输出结果为 4。var getName // 函数表达式会拆成两行代码执行function getName() {console.log(5)} // 函数声明被提升,覆盖getName值getName = function () {console.log(4)} // 函数表达式的赋值,再次覆盖函数声明的值。 -
Foo().getName()先执行了 Foo 函数,然后调用 Foo 函数的返回值对象的 getName 属性函数。Foo 函数返回了 this,这个 this 是函数调用,默认会绑定到全局对象 window,所以 Foo().getName() 相当于执行 window.getName() 方法。但是 Foo 函数内部的 getName 并没有 var 声明,所以 Foo 函数内部的 getName 函数赋值会重新赋值覆盖全局变量,所以输出 1。 -
由于上面代码修改了全局变量 getName 变量,此时全局变量 getName 已变成
function() {console.log(1)},结果为 1。 -
new Foo.getName()考察的是 js 的运算符优先级。运算符的优先级可以查看 Operator precedence。相当于new (Foo.getName)()。实则将 Foo.getName 函数作为构造函数执行。答案是 2 -
new Foo().getName()运算符优先级括号高于 new,实际执行为(new Foo()).getName()。所以当调用 getName 方法的时候去原型链上找到结果 3。PS:Foo.getName 是绑定 Foo 上面的一个属性方法,实例化对象的时候不会存在。 -
new new Foo().getName()运算符优先级转化后new ((new Foo()).getName())。先初始化 Foo 的实例化对象,然后将其原型上的 getName 函数作为构造函数再实例化。结果为 3。