资讯专栏INFORMATION COLUMN

使用 new 操作符内部到底在做什么

curlyCheng / 3263人阅读

摘要:把方法移动到构造函数外部把方法移至外面,这样每次实例化内部的只是全局的引用,这样避免了重复。构造函数什么体内什么都没有,如果有叫做实例方法,实力属性缺点重复敲,造成大量的重复输入。

从对象声明开始一步步介绍

1.普通对象声明

首先创建自定义对象的最简单方式就是创建一个Object的实例,然后在为他们添加属性和方法,如下所示:

var person = new Object();        //创建对象
person.name = "Nicholas";        //添加属性
person.age = 29;
person.job = "teacher";
person.sayName = function(){    //添加方法
    return this.name
};

this的含义:

this表示当前作用域下的对象;

this表示new Object()实例化出来的那个对象;

this要放在一个作用域下,比如person.sayName()person下的方法,可用this表示方法本身。

缺点:要创建一个类似的对象会产生大量的代码。

为了解决多个类似声明的问题,用一种工厂模式,这种方法是为了解决实例化对象产生大量重复的代码。

2.工厂模式

用函数来封装以特定接口创建对象的细节。

function createPerson(name,age,job){    //创建对象
    var obj = new Object();                //添加属性
    obj.name = name;
    obj.age = age;
    obj.job = job;
    obj.sayName = function(){            //添加方法
        return this.name
    };
    return obj;                            //返回对象引用
}

var person1 = createPerson("Zhangsan",29,"Teacher");    //实例化第一个对象
var person2 = createPerson("Lisi",34,"Doctor");        //实例化第二个对象
console.log(person2 instanceof Object)        //true

this的含义:
1.thisnew Object(),实例化出来的那个对象;
2.this要放在一个作用域下,比如obj.sayName(){},这是obj作用域下的的方法,可以用this来表示obj本身。

缺点:集中实例化函数,解决了大量重复的代码;从上面例子我们可以看出sayName是共有属性,而我们每实例化一个函数都会创建sayName,这也造成了重复。

3.构造函数模式

构造函数可用来创建特定类型的对象,类似Object类型。

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        return this.name
    };
}

function Person2(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        return this.name
    };
}

var person1 = new Person("Zhangsan",29,"Teacher");
var person2 = new Person("Lisi",34,"Doctor");    
var person3 = new Person2("Wangwu",34,"Police");

alert(person1 instanceof Person);        //true,person1从属于Person
alert(person2 instanceof Person);        //true,person2从属于Person
alert(person3 instanceof Person2);        //true,person3从属于Person2
alert(person1 instanceof Person2);        //false,因为这里person1是从属于Person

alert(person1.sayName() == person2.sayName());    //true,构造函数的方法的值是想等的
alert(person1.sayName == person2.sayName);    //false,比较的是引用地址

我们使用new操作符,到底是在做什么

不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);

不用绑定原型,因为 new 会帮你做(new为了知道原型在哪,所以指定原型的名字为 prototype);

不用 return 临时对象,因为 new 会帮你做;

不要给原型想名字了,因为 new 指定名字为 prototype。

persen1 和 person2 的 constructor 属性都指向 Person

缺点:每次实例化 Person,共有属性 sayName 都会重复创建,和工厂模式问题一样。

3.1把方法移动到构造函数外部

把 sayName 方法移至 Person 外面,这样每次实例化 Person 内部的 sayName 只是全局 sayName 的引用,这样避免了重复。

function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName(){            //把构造函数内部的方法通过全局来实现,引用地址一致
    return this.name
}

var person1 = new Person("Zhangsan",29,"Teacher");
var person2 = new Person("Lisi",34,"Doctor");    

缺点:

全局 sayName 函数和 Person 之间联系不紧密,如果它们中间有很多代码,sayName 就不知道是干嘛用的了;

如果方法很多,每个都是全局函数,就没封装可言了;

用全局函数很容易覆盖全局变量。

4.原型模式

使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

如果是实例方法,不同的实例化,它们的方法和地址是不一样的,是唯一的;

如果是原型方法,那它们的地址是共享的,大家都一样。

function Person(){}        //构造函数什么体内什么都没有,如果有叫做实例方法,实力属性

Person.prototype.name = "Zhangsan";
Person.prototype.age = 29;
Person.prototype.job = "Teacher";
Person.prototype.sayName = function(){
    return this.name
};

var person1 = new Person();
person.sayName()     //Zhangsan

var person2 = new Person();
person.sayName()     //Zhangsan

alert(person1.sayName === person2.sayName);    //true

缺点:重复敲Person.prototype,造成大量的重复输入。

4.1字面量方式创建原型
function Person(){}//使用字面量的方式创建原型对象,这里的`{}`就是对象,是`Object`,`new Object`相当于`{}`

Person.prototype = {
    constructor:Person,    //强行指向实例
    name: "Zhangsan",
    age: 29,
    job: "Teacher",
    sayName: function(){
        return this.name
    }
};

var person = new Person();

注意:

实例化后重写原型对象,会切断现有实例和新原型之间的联系

不能重写原型中的属性,如 person.name = "Lisi",它会变成 person 的实例属性。

缺点:constructor不在指向实例,而会指向Object。新对象的constructor重写Person原来的constructor,因此会指向新对象。
解决方法:在原型内部,可以设置constructor强行执行实例。

4.2组合构造函数模式和原型模式

构造函数模式用于定义实力属性,原型模式用于定义方法和共享属性

function Person(name,age,job){        //保持独立的使用构造函数
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Xiaoming","Fangfang"];
}

Person.prototype = {        //保存共享的使用原型
    constructor: Person,
    sayName: function(){
        return this.name
    }
}

var person1 = new Person("Zhangsan",29,"Teacher");
var person2 = new Person("Wangwu",34,"Doctor");

person1.friends.push("Xiaohong");
alert(person1.friends);    //"Xiaoming,Fangfang,Xiaohong"
alert(person2.friends);    //"Xiaoming,Fangfang",引用类型没有使用原型,所以没有共享
alert(person1.friends == person2.friends);    //false
alert(person1.sayName == person2.sayName);    //true

注意:实例化的私有属性是自有的

5.动态原型模式

动态原型模式,把所有信息都封装在了构造函数中。

function Person(name,age,job){        //保持独立的使用构造函数
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Xiaoming","Fangfang"];
    
    if(typeof this.sayName != "function"){    //仅在第一次时初始化
        Person.prototype.sayName = function(){
            console.log(this.name);
        };
    }
}

原型的初始化,只要第1次初始化,就可以了,没必要每次构造函数实例化的时候都初始化,可以将原型封装在函数里。
注意:使用动态原型模式时,不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

proto 和 prototype

__proto__:是实例化后的原型属性
prototype:是 JS 内部提供的原型属性

上面例子中
person1.__proto__ === Person.prototype
person1.__proto__.__proto__ === Object.prototype

之前写过一篇文章阐述它们之间的不同:前端学习笔记之原型——一张图说明prototype__proto__的区别

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94546.html

相关文章

  • 当我们在JavaScript中new一个对象的时候,我们到底在做什么

    摘要:当构造函数没有显式地返回一个值的时候,对其执行操作之后,会返回这个构造函数实例化之后的对象。 JavaScript里实例化一个对象的时候,我们常用的方法就是使用new操作符。 var Foo = function(x, y) { this.x = x this.y = y } var foo = new Foo(1, 2) // Foo {x: 1, y: 2} 那么...

    woshicixide 评论0 收藏0
  • 继承与原型

    摘要:既然构造函数有属于自己的原型对象,那么我们应该能让另一个构造函数来继承他的原型对象咯我们在构造函数内部执行了函数并改变了函数内部的指向其实这个指向的是实例化之后的对象。 我们在讨(mian)论(shi)JavaScript这门语言时,总是绕不过的一个话题就是继承与原型链。那么继承与原型链到底是什么呢? 我很喜欢的一个聊天模式是:我不能说XX是什么,我只能说XX像什么。也就是说我不直接跟...

    My_Oh_My 评论0 收藏0
  • 自己实现一个简单的虚拟 DOM

    摘要:直到内部的全部循环结束为止,才进入下一个元素,当循环结束时,内部的节点都已经生成好了。 自己实现虚拟 DOM 从 HTML 中提炼数据结构 先来看下我们的 HTML 傅雷家书 读家书,想付雷 从 HTML 中我们可以抽离出它的数据结构: 首先页面中只需要一个根节点root,定义为:nodesDate数组 root内有两个子元素h1和span,数组有两项,每项为内...

    luffyZh 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<