从了解new出发,手写代码(1)

什么是new?

new 运算符建立一个用户定义的对象类型的实例或具备构造函数的内置对象的实例数组

new作了什么?

  1. 建立一个空的简单JavaScript对象(即{});
  2. 连接该对象(即设置该对象的构造函数)到另外一个对象 ;
  3. 将步骤1新建立的对象做为this的上下文 ;
  4. 若是该函数没有返回对象,则返回this。

new的实现过程

读懂个人代码

其实咱们了解了new的整个过程咱们是比较容易去实现一个new的 1.let obj = {} 或者 let obj = new object(); 二者等价 2.obj.proto = Func.prototype (Func为构造函数) 3.经过apply改变this,以访问函数内部变量 4.返回bash

function New() {
    let obj = {}; // 建立对象
    console.log(arguments);
    let constructor =  [].shift.call(arguments); // 获取构造函数
    console.log(arguments);
    if (constructor.prototype !== null) {
        obj.__proto__ = constructor.prototype; // 构造函数连接到新对象
    }
    let ret = constructor.apply(obj, [].slice.call(arguments)); // 改变this指向
    console.log(arguments);
    console.log(typeof ret);
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return obj; // 若是函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用。
}

function name(a, b) {
	this.a = a;
	this.b = b;
}

let c = New(name, 1, 2)
let d = new name(1, 2)
console.log(c);
console.log(d);
复制代码

咱们来看返回值:app

咱们看到New 和 new产生了一样的效果函数

注意:[].shift.slice()会改变咱们的arguments。typeof null == "object"性能

重点:为何要进行对象判断,通常实现new时会把这一点的解释给忽略。优化

默认状况下函数的返回值为undefined(即没有显示地定义返回值的话),可是构造函数比较例外,new构造函数在没有return的状况下默认返回新建立的对象。可是在有显示返回值的状况下,若是返回值为基本数据类型的话(string,number,boolean,undefined,null),返回值仍然为新建立的对象,这一点比较奇怪,须要注意。只有在显示返回一个非基本数据类型的对象的时候,函数的返回值才为指定的对象。在这种状况下,this值所引用的对象就被丢弃了。ui

看下面两个例子:this

例1: spa

例2: prototype

// return; // 返回 this

// return null; // 返回 this

// return this;

// return []; // 返回 []

// return function(){}; // 返回 这个 function,抛弃 this

// return false; // 返回 this

// return new Boolean( false); // 返回新 boolean;抛弃 this

// return 'hello world'; // 返回 this

// return new String( 'hello world'); // 返回 新建的 string,抛弃 this

// return 2; // 返回 this

// return new Number( 32); // 返回新的 number,抛弃 this

arguments是什么?

arguments 是一个对应于传递给函数的参数的类数组对象。

arguments 对象只能在函数内使用

[].slice.call()作了什么?

将arguments转换成数组

相似的转换方法

var args = Array.prototype.slice.call(arguments);

var args = [].slice.call(arguments);

// ES2015

const args = Array.from(arguments);

const args = [...arguments];

MDN上不建议咱们对参数进行slice 解决方案:

function New() {
    let obj = {}; // 建立对象
    console.log(arguments);
    let constructor =  [].shift.call(arguments); // 获取构造函数
    console.log(arguments);
    if (constructor.prototype !== null) {
        obj.__proto__ = constructor.prototype;
    }
    let ret = constructor.apply(obj, (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments)));
    console.log(ret);
    console.log(arguments);
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return obj;
}

function name(a, b) {
	this.a = a;
	this.b = b;
}

let c = New(name, 1, 2)
let d = new name(1, 2)
console.log(c);
console.log(d);
复制代码

对参数使用slice会阻止某些JavaScript引擎中的优化。若是你关心性能,尝试经过遍历arguments对象来构造一个新的数组。另外一种方法是使用被忽视的Array构造函数做为一个函数

[].shift.call()作了什么?

获取arguments的第一个参数,改变arguments的length

slice作了什么

返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。

.slice()方法

定义和用法 slice(start, end) 方法可提取数组的某个部分,并以新的数组返回被提取的部分。

使用start(包含) 和 end(不包含)

参数来指定提取数组开始和结束的部分。

若是未指定start和end,则返回整个数组。

若是指指定一个参数,该参数做为start使用,返回包括start位置以后的所有数组。

若是是负数,则该参数规定的是从数组的尾部开始算起的位置。也就是说,-1 指数组的最后一项,-2 指倒数第二个项,以此类推。

call作了什么(具体实现会将call,apply,bind放在一块儿讲)

改变[]中的this指向

apply作什么(具体实现会将call,apply,bind放在一块儿讲)

改变this的指向

this.指向如何改变

经过 apply指向 apply将构造函数的this指向新建立的对象

将具备length属性的对象转换为数组的方法

array.form() 或者 ... 或则遍历

array.from转换时须要注意什么

类数组对象的key值为数字

slice如何实现?

Array.prototype.slice = function(start,end){
     var result = new Array();
     start = start || 0;
     end = end || this.length; //this指向调用的对象,当用了call后,可以改变this的指向,也就是指向传进来的对象,这是关键
     for(var i = start; i < end; i++){
          result.push(this[i]);
     }
     return result;
复制代码
相关文章
相关标签/搜索