JavaScript 面向对象之一:构造函数

构造函数意义

JavaScript 语言使用构造函数作为对象的模板。
所谓 ”构造函数”,就是一个函数,专门用它来生成对象;

类似与Java中的作用;
它提供模板,描述对象的基本结构。
一个构造函数,可以生成多个对象,这些对象都有相同的结构。

function Person (name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
}

var p1 = new Person('Jack', 18)
p1.sayName() // => Jack

var p2 = new Person('Mike', 23)
p2.sayName() // => Mike

解析构造函数代码执行

在上面的示例中,Person() 函数取代了 createPerson() 函数,但是实现效果是一样的。
这是为什么呢?

我们注意到,Person() 中的代码与 createPerson() 有以下几点不同之处:

  • 没有显示的创建对象
  • 直接将属性和方法赋给了 this
  • 没有 return 语句
  • 函数名使用的是大写的 Person

而要创建 Person 实例,则必须使用 new 操作符。

以这种方式调用构造函数会经历以下 5 个步骤:

  1. 创建一个空对象,作为将要返回的对象实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。
  3. 将这个空对象赋值给函数内部的this关键字。
  4. 执行构造函数内部的代码。
  5. 返回新对象
function Person (name, age) {
  // 当使用 new 操作符调用 Person() 的时候,实际上这里会先创建一个对象
  // 然后让内部的 this 指向新创建的对象
  // 接下来所有针对 this 的操作实际上操作的就是刚创建的这个对象

  this.name = name
  this.age = age
  this.sayName = function () {
      console.log(this.name)
  }

  // 在函数的结尾处会将 this 返回,也就是这个新对象
}

构造函数和实例对象的关系

构造函数是根据具体的事物抽象出来的抽象模板

实例对象是根据抽象的构造函数模板得到的具体实例对象

实例对象由构造函数而来,一个构造函数可以生成很多具体的实例对象,而每个实例对象都是独一无二的;

每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数

反推出来,每一个对象都有其构造函数

console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true

因此,我们可以通过实例对象的 constructor 属性判断实例和构造函数之间的关系

注意:这种方式不严谨,推荐使用 instanceof 操作符,因为对象的__proto__可以被强制改变

console.log(p1 instanceof Person) // => true
console.log(p2 instanceof Person) // => true
  • constructor 既可以判断也可以获取

  • instanceof 只能用于判断

构造函数存在的问题

以构造函数为模板,创建对象,对象的属性和方法都可以在构造函数内部定义;

function Cat(name, color) {
    this.name = name;
    this.color = color;
    this.say = function () {
        console.log('hello'+this.name,this.color);
    };
}
var cat1 = new Cat('猫', '白色'); 
var cat2 = new Cat('猫', '黑色'); 
cat1.say();
cat2.say();

在该示例中,从表面上看好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象, namesay 都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

对于这种问题我们可以把需要共享的函数定义到构造函数外部:

function say(){
    console.log('hello'+this.name,this.color);
}

function Cat(name, color) {
    this.name = name;
    this.color = color;
    this.say = say;
}
var cat1 = new Cat('猫', '白色'); 
var cat2 = new Cat('猫', '黑色'); 
cat1.say();
cat2.say();

这样确实可以了,但是如果有多个需要共享的函数的话就会造成全局命名空间及变量冲突的问题。

可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:

var s = {
    sayhello:function (){
        console.log('hello'+this.name,this.color);
    },
    saycolor:function(){
        console.log('hello'+this.color);
    }
}

function Cat(name, color) {
    this.name = name;
    this.color = color;
    this.sayhello = s.sayhello;
    this.saycolor = s.saycolor;
}
var cat1 = new Cat('猫', '白色'); 
var cat2 = new Cat('猫', '黑色'); 
cat1.sayhello();
cat2.saycolor();

猜你喜欢

转载自blog.csdn.net/github_27314097/article/details/81637401
今日推荐