js constructor understanding

As the basis of prototype and prototype chain, first understanding the constructor and its execution process can better help us learn the knowledge of prototype and prototype chain.

1. What is a constructor

In JavaScript, a function called with the new keyword is called a constructor. The first letter of the constructor is usually capitalized

2. Why use a constructor?

Learn each concept, not only what it is, but why, and what kind of problem it solves.

For example, we want to enter the personal information of each classmate in the first grade, then we can create some objects, such as:

var p1 = { name: 'zs', age: 6, gender: '男', hobby: 'basketball' };
var p2 = { name: 'ls', age: 6, gender: '女', hobby: 'dancing' };
var p3 = { name: 'ww', age: 6, gender: '女', hobby: 'singing' };
var p4 = { name: 'zl', age: 6, gender: '男', hobby: 'football' }; 
// ...
复制代码

Like the above, we can treat each classmate's information as an object. However, we will find that we repeatedly write a lot of meaningless code. Such as name, age, gender, hobby. If there are 60 students in this class, we have to repeat it 60 times.

At this time, the advantages of constructors are reflected. We found that although each classmate has attributes such as name, gender, and hobby, they are all different, so we pass these attributes as parameters of the constructor. Since they are all first-year students, the age is basically 6 years old, so we can write to death, and we can deal with it separately in case of special circumstances. At this point, we can create the following function:

function Person(name, gender, hobby) {
    this.name = name;
    this.gender = gender;
    this.hobby = hobby;
    this.age = 6;
} 
复制代码

When the above function is created, we can call it through the new keyword, that is, create an object through the constructor.

var p1 = new Person('zs', '男', 'basketball');
var p2 = new Person('ls', '女', 'dancing');
var p3 = new Person('ww', '女', 'singing');
var p4 = new Person('zl', '男', 'football');
// ... 
复制代码

At this point you will find that creating objects becomes very convenient. So, although the process of encapsulating the constructor will be more troublesome, once the encapsulation is successful, it will be very easy for us to create the object, which is why we use the constructor.

When using object literals to create a series of objects of the same type, these objects may have some similar characteristics (properties) and behaviors (methods), which will result in a lot of repetitive code, which can be achieved by using constructors 代码复用.

3. The execution process of the constructor

Let's talk about some basic concepts first.

function Animal(color) {
 this.color = color;
} 
复制代码

When a function is created, we don't know if it is a constructor or not, even if the function name is capitalized like in the above example, we can't be sure. We can say that a function is a constructor only when it is called with the new keyword. Like the following:

var dog = new Animal("black"); 
复制代码

Below we only discuss the execution process of the constructor, that is, the case of calling with the new keyword.

我们还是以上面的 Person 为例。

function Person(name, gender, hobby) {
 this.name = name;
 this.gender = gender;
 this.hobby = hobby;
 this.age = 6;
}

var p1 = new Person('zs', '男', 'basketball'); 
复制代码

此时,构造函数会有以下几个执行过程:

(1) 当以 new 关键字调用时,会创建一个新的内存空间,标记为 Animal 的实例。

(2)函数体内部的 this 指向该内存

通过以上两步,我们就可以得出这样的结论。

var p2 = new Person('ls', '女', 'dancing');  // 创建一个新的内存 #f2
var p3 = new Person('ww', '女', 'singing');  // 创建一个新的内存 #f3 
复制代码

每当创建一个实例的时候,就会创建一个新的内存空间(#f2, #f3),创建 #f2 的时候,函数体内部的 this 指向 #f2, 创建 #f3 的时候,函数体内部的 this 指向 #f3。

(3) 执行函数体内的代码
通过上面的讲解,你就可以知道,给 this 添加属性,就相当于给实例添加属性。

  1. 默认返回 this

由于函数体内部的this指向新创建的内存空间,默认返回 this ,就相当于默认返回了该内存空间,也就是上图中的 #f1。此时,#f1的内存空间被变量p1所接受。也就是说 p1 这个变量,保存的内存地址就是 #f1,同时被标记为 Person 的实例。

以上就是构造函数的整个执行过程。

4、构造函数的返回值

构造函数执行过程的最后一步是默认返回 this 。言外之意,构造函数的返回值还有其它情况。下面我们就来聊聊关于构造函数返回值的问题。

(1) 没有手动添加返回值,默认返回 this

function Person1() {
 this.name = 'zhangsan';
}

var p1 = new Person1(); 
复制代码

按照上面讲的,我们复习一遍。首先,当用 new 关键字调用时,产生一个新的内存空间 #f11,并标记为 Person1 的实例;接着,函数体内部的 this 指向该内存空间 #f11;执行函数体内部的代码;由于函数体内部的this 指向该内存空间,而该内存空间又被变量 p1 所接收,所以 p1 中就会有一个 name 属性,属性值为 ‘zhangsan’。 

p1: {
 name: 'zhangsan'
} 
复制代码

(2) 手动添加一个基本数据类型的返回值,最终还是返回 this

function Person2() {
 this.age = 28;
 return 50;
}

var p2 = new Person2();
console.log(p2.age);   // 28
p2: {
 age: 28
} 
复制代码

如果上面是一个普通函数的调用,那么返回值就是 50。

(3) 手动添加一个复杂数据类型(对象)的返回值,最终返回该对象

直接上例子

function Person3() {
 this.height = '180';
 return ['a', 'b', 'c'];
}

var p3 = new Person3();
console.log(p3.height);  // undefined
console.log(p3.length);  // 3
console.log(p3[0]);      // 'a' 
复制代码

再来一个例子

function Person4() {
  this.gender = '男';
  return { gender: '中性' };
}

var p4 = new Person4();
console.log(p4.gender);  // '中性' 
复制代码

5、构造函数首字母必须大写吗?

大小写都可以

6、不用new关键字,直接运行构造函数,是否会出错?如果不会出错,那么,用new和不用new调用构造函数,有什么区别?

  1. 使用new操作符调用函数

例子:

function Person(name){
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
}

var person1 = new Person('nicole');
person1.say(); // "I am nicole" 
复制代码

用new调用构造函数,函数内部会发生如下变化:

创建一个this变量,该变量指向一个空对象。并且该对象继承函数的原型;
属性和方法被加入到this引用的对象中;
隐式返回this对象(如果没有显性返回其他对象)
用伪程序来展示上述变化:

function Person(name){
  // 创建this变量,指向空对象
  var this = {}; 
  // 属性和方法被加入到this引用的对象中
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
  // 返回this对象
  return this;
} 
复制代码

可以看出,用new调用构造函数,最大特点为,this对象指向构造函数生成的对象,所以,person1.say()会返回字符串: “I am nicole”。

小贴士

如果指定了返回对象,那么,this对象可能被丢失。

function Person(name){
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
  var that = {};
  that.name = "It is that!";
  return that;
}

var person1 = new Person('nicole');
person1.name; // "It is that!" 
复制代码

  2. 直接调用函数

如果直接调用函数,那么,this对象指向window,并且,不会默认返回任何对象(除非显性声明返回值)。

还是拿Person函数为例,直接调用Person函数:

var person1 = Person('nicole');
person1; // undefined
window.name; // nicole 
复制代码

可见,直接调用构造函数的结果,并不是我们想要的。

3.小结

为了防止因为忘记使用new关键字而调用构造函数,可以加一些判断条件强行调用new关键字,代码如下

function Person(name){
  if (!(this instanceof Person)) {
    return new Person(name);
  }
  this.name = name;
  this.say = function(){
    return "I am " + this.name;
  }
}

var person1 = Person('nicole');
console.log(person1.say()); // I am nicole
var person2 = new Person('lisa');
console.log(person2.say()); // I am lisa
复制代码

Guess you like

Origin juejin.im/post/6955051085409026078