前端基础-JavaScript构造函数

第2章 构造函数

学习目标

  • 构造函数语法
  • 分析构造函数
  • 构造函数和实例对象的关系
    • 实例的 constructor 属性
    • instanceof 操作符
  • 普通函数调用和构造函数调用的区别
  • 构造函数的返回值
  • 构造函数的问题

2.1 构造函数

JavaScript 语言使用构造函数作为对象的模板。
所谓 ”构造函数”,就是一个普通的函数,只不过我们专门用它来生成对象(new 构造函数),这样使用的函数,就是构造函数;

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

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 操作符,后面学原型会解释为什么

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

constructor 既可以判断也可以获取

instanceof 只能用于判断

2.2 构造函数存在的问题

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

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();

至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。
但是代码看起来还是那么的格格不入,那有没有更好的方式呢?

小结

  • 构造函数语法
  • 分析构造函数
  • 构造函数和实例对象的关系
    • 实例的 constructor 属性
    • instanceof 操作符
  • 构造函数的问题
发布了1775 篇原创文章 · 获赞 1895 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/weixin_42528266/article/details/105112221