zoukankan      html  css  js  c++  java
  • Javascript中构造函数与new命令

    典型的面向对象编程语言(比如C++和Java),存在“类”(class)这个概念。所谓“类”就是对象的模板,对象就是“类”的实例。但是,在JavaScript语言的对象体系,不是基于“类”的,而是基于构造函数(constructor)和原型链(prototype)。

    以下的内容会分为如下细节:

    1.对象的概念

    2.构造函数

    3.new 命令

      3.1:基本原理

      3.2:基本用法

    1.对象的概念

      “面向对象编程”(Object Oriented Programming,缩写为OOP)是目前主流的编程范式。它的核心思想是将真实世界中各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。

      在Javascript的重要数据类型-对象这篇文章中,提到了js中对象的一些基本知识,比如说对象的创建,对象的引用,对象的属性等等。如果没有掌握对象的基础知识,请移步Javascript的重要数据类型-对象

      在有了对象的基础知识之后,对js中对象做一个简单的总结。如下:

      a:对象是单个实物的抽象。

      b:对象是一个容器,封装了‘属性’和‘方法’。

      一本书、一辆汽车、一个人都可以是“对象”。当实物被抽象成“对象”,实物之间的关系就变成了“对象”之间的关系,从而就可以模拟现实情况,针对“对象”进行编程。

      所谓属性,就是对象的一种状态;所谓方法,就是对象的一种行为。

      比如说,可以把动物抽象为animal对象,属性记录的就是哪一种动物,以及该动物的大小和颜色等。方法表示该动物的某种行为(奔跑,猎食,交配,休息等等)。

    2.构造函数

      ‘面向对象编程’的第一步,就是要生成对象。而js中面向对象编程是基于构造函数(constructor)和原型链(prototype)的。

      前面说过,“对象”是单个实物的抽象。通常需要一个模板,表示某一类实物的共同特征,然后“对象”根据这个模板生成。

      js语言中使用构造函数(constructor)作为对象的模板。所谓构造函数,就是提供一个生成对象的模板,并描述对象的基本结构的函数。一个构造函数,可以生成多个对象,每个对象都有相同的结构。

      看一下构造函数的基本结构。

    复制代码
    1     var Keith = function() {
    2         this.height = 180;
    3     };
    4     //两种写法相同。
    5     function Keith() {
    6         this.height = 180;
    7     }
    复制代码

      上面代码中,Keith就是构造函数,它提供模板,用来生成对象实例。为了与普通函数区别,构造函数名字的第一个字母通常大写。

      构造函数的三大特点:

      a:构造函数的函数名的第一个字母通常大写。

      b:函数体内使用this关键字,代表所要生成的对象实例。

      c:生成对象的时候,必须使用new命令来调用构造函数。

    3.new 命令

      3.1:基本原理

      new命令的作用,就是执行一个构造函数,并且返回一个对象实例。使用new命令时,它后面的函数调用就不是正常的调用,而是依次执行下面的步骤。

      a:创建一个空对象,作为将要返回的对象实例。

      b:将空对象的原型指向了构造函数的prototype属性。

      c:将空对象赋值给构造函数内部的this关键字。

      d:开始执行构造函数内部的代码。

      也就是说,构造函数内部,this指向的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。构造函数之所谓构造函数,意思是这个函数的目的就是操作一个空对象(即this对象),将其构造为需要的样子。

      以上是new命令的基本原理,这个很重要。以下会用具体实例来验证该原理的过程。

      

      3.2:基本用法

      new命令的作用,就是调用一个构造函数,并且返回一个对象实例。

    复制代码
    1     function Keith() {
    2         this.height = 180;
    3     }
    4 
    5     var boy = new Keith();
    6     console.log(boy.height);  //180
    复制代码

      上面代码中通过new命令,让构造函数Keith生成一个对象实例,并赋值给全局变量boy。这个新生成的对象实例,从构造函数Keith中继承了height属性。也就说明了这个对象实例是没有height属性的。在new命令执行时,就代表了新生成的对象实例boy。this.height表示对象实例有一个height属性,它的值是180。

      使用new命令时,根据需要,构造函数也可以接受参数。

    复制代码
     1     function Person(name, height) {
     2         this.name = name;
     3         this.height = height;
     4     }
     5 
     6     var boy = new Person('Keith', 180);
     7     console.log(boy.name); //'Keith'
     8     console.log(boy.height); //180
     9 
    10     var girl = new Person('Samsara', 160);
    11     console.log(girl.name); //'Samsara'
    12     console.log(girl.height); //160
    复制代码

      用以上的一个例子,来对构造函数的特点和new基本原理进行一个梳理。

      上面代码中,首先,我们创建了一个构造函数Person,传入了两个参数name和height。构造函数Person内部使用了this关键字来指向将要生成的对象实例。

      然后,我们使用new命令来创建了两个对象实例boy和girl。

      当我们使用new来调用构造函数时,new命令会创建一个空对象boy,作为将要返回的实例对象。接着,这个空对象的原型会指向构造函数Person的prototype属性。也就是boy.__proto__ prototype===Person.prototype的。要注意的是空对象指向构造函数Person的prototype属性,而不是指向构造函数本身。然后,我们将这个空对象赋值给构造函数内部的this关键字。也就是说,让构造函数内部的this关键字指向一个对象实例。最后,开始执行构造函数内部代码。

      因为对象实例boy和girl是没有name和height属性的,所以对象实例中的两个属性都是继承自构造函数Person中的。这也就说明了构造函数是生成对象的函数,是给对象提供模板的函数。

      

      一个问题,如果我们忘记使用new命令来调用构造函数,直接调用构造函数了,会发生什么?

      这种情况下,构造函数就变成了普通函数,并不会生成实例对象。而且由于后面会说到的原因,this这时代表全局对象,将造成一些意想不到的结果。

    复制代码
    1     function Keith() {
    2         this.height = 180;
    3     }
    4 
    5     var person = Keith();
    6     console.log(person.height); //TypeError: person is undefined
    7     console.log(window.height); //180
    复制代码

      上面代码中,当在调用构造函数Keith时,忘记加上new命令。结果是this指向了全局作用域,height也就变成了全局变量。而变量person变成了undefined。

      因此,应该非常小心,避免出现不使用new命令、直接调用构造函数的情况。

      为了保证构造函数必须与new命令一起使用,一个解决办法是,在构造函数内部使用严格模式,即第一行加上use strict

    复制代码
    1     function Person(name, height) {
    2         'use strict';
    3         this.name = name;
    4         this.height = height;
    5     }
    6     var boy = Person();
    7     console.log(boy) //TypeError: name is undefined
    复制代码

      上面代码的Person为构造函数,use strict命令保证了该函数在严格模式下运行。由于在严格模式中,函数内部的this不能指向全局对象。如果指向了全局,this默认等于undefined,导致不加new调用会报错(JavaScript不允许对undefined添加属性)。

      另一个解决办法,是在构造函数内部判断是否使用new命令,如果发现没有使用,则直接返回一个实例对象。

    复制代码
    1     function Person(name, height) {
    2     if (!(this instanceof Person)) {
    3             return new Person(name, height);
    4         }
    5         this.name = name;
    6         this.height = height;
    7     }
    8     var boy = Person('Keith');
    9     console.log(boy.name) //'Keith'
    复制代码

      上面代码中的构造函数,不管加不加new命令,都会得到同样的结果。

      如果构造函数内部有return语句,而且return后面跟着一个复杂数据类型(对象,数组等),new命令会返回return语句指定的对象;如果return语句后面跟着一个简单数据类型(字符串,布尔值,数字等),则会忽略return语句,返回this对象。

    复制代码
     1     function Keith() {
     2         this.height = 180;
     3         return {
     4             height: 200
     5         };
     6     }
     7     var boy = new Keith();
     8     console.log(boy.height); //200
     9 
    10     function Keith() {
    11         this.height = 100;
    12         return 200;
    13     }
    14     var boy = new Keith();
    15     console.log(boy.height); //100
    复制代码

      另一方面,如果对普通函数(内部没有this关键字的函数)使用new命令,则会返回一个空对象。

    1     function Keith() {
    2         return 'this is a message';
    3     }
    4     var boy = new Keith();
    5     console.log(boy); // Keith {}

      上面代码中,对普通函数Keith使用new命令,会创建一个空对象。这是因为new命令总是返回一个对象,要么是实例对象,要么是return语句指定的对象或数组。本例中,return语句返回的是字符串,所以new命令就忽略了该语句。

  • 相关阅读:
    【校招面试 之 C/C++】第23题 C++ STL(五)之Set
    Cannot create an instance of OLE DB provider “OraOLEDB.Oracle” for linked server "xxxxxxx".
    Redhat Linux安装JDK 1.7
    ORA-10635: Invalid segment or tablespace type
    Symantec Backup Exec 2012 Agent for Linux 卸载
    Symantec Backup Exec 2012 Agent For Linux安装
    You must use the Role Management Tool to install or configure Microsoft .NET Framework 3.5 SP1
    YourSQLDba介绍
    PL/SQL重新编译包无反应
    MS SQL 监控数据/日志文件增长
  • 原文地址:https://www.cnblogs.com/chenliyang/p/6548351.html
Copyright © 2011-2022 走看看