我们在学习「预编译」的时候知道函数执行this存在,AO保存了this对象,this指向window

  1. function Car(color) {
  2. // window.color
  3. this.color = color;
  4. }
  5. Car();

this在没有实例化的时候,它的指向是window,一旦实例化了构造函数以后,this指向实例化对象。
**new**的作用就是创建**this**对象然后把**this**返回指向实例化对象。

  1. // 不实例化对象的时候 this 指向 window
  2. function Car(color, brand) {
  3. this.color = color;
  4. this.brand = brand;
  5. }
  6. var car1 = new Car("red", "Benz");
  7. var car2 = new Car("black", "Mazda");
  8. console.log(car1.color, car2.color); // red black

car1car2都用的是this,但是数据不一样,证明this并不是指向window,而指向实例化对象。

那么「构造函数」中的this到底是怎么回事?

构造函数的原理

先看一段代码:

  1. function Car(color, brand) {
  2. this.color = color;
  3. this.brand = brand;
  4. }
  5. var car1 = new Car("red", "Benz");
  6. console.log(car1.color);
  7. /**
  8. * 代码执行先创建 GO
  9. * GO = {
  10. * Car: function,
  11. * car1: undefind
  12. * }
  13. *
  14. * new Car() 的时候就相当于函数执行,函数执行就会创建 AO
  15. * AO = {
  16. *
  17. * }
  18. *
  19. * 因为 new 关键字的存在,Car() 就会变成构造函数,构造函数的 AO 默认创建 this 对象
  20. * new 关键字创建了 this 对象,最后 return this
  21. * AO = {
  22. * this:{}
  23. * }
  24. *
  25. * 开始执行构造函数,构造函数内 this 赋值
  26. * AO = {
  27. * this:{
  28. * color: color
  29. * brand: brand
  30. * }
  31. * }
  32. *
  33. * 赋值完成后 return this 对象,赋值给 car1
  34. * GO = {
  35. * Car: function,
  36. * car1: this
  37. * }
  38. *
  39. * 所以可以访问 car1.color
  40. *
  41. */

我们知道了「构造函数」的执行过程能不能自己模拟一个new的过程呢?

  1. function Car(color, brand) {
  2. var me = {};
  3. me.color = color;
  4. me.brand = brand;
  5. return me;
  6. }
  7. var car1 = Car("red", "Benz");
  8. console.log(car1.color);

new构造函数的时候需要注意

  • 当显式return引用值的时候返回引用值
  • 如果显式return原始值则依然返回this对象 ```javascript // 返回引用值 function Car() { this.color = “red”; this.brand = “Benz”; // return {}; // return []; return function () {}; } var car1 = new Car(); console.log(car1); // function

// 返回原始值 function Car() { this.color = “red”; this.brand = “Benz”; return 123; } var car1 = new Car(); console.log(car1, car1.color); // Car {color: ‘red’, brand: ‘Benz’}, red

  1. <a name="a52sM"></a>
  2. ## 包装类
  3. `JavaScript`有三个特殊的「原始类型」,分别是`String`、`Number`、`Boolean`
  4. 我们都知道「引用类型」本身是有属性和方法的,比如:
  5. ```javascript
  6. var arr = [1, 2, 3];
  7. arr.push(4);
  8. arr.length;

但是如String这样的原始类型为什么也会有lengthsubString()等属性和方法呢?
其实这是因为每当String调用方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的各种方法。

  1. let s1 = "some text";
  2. let s2 = s1.substring(2);

包装类型对象的过程分为 3 步:

  1. 创建一个 String 类型的实例;
  2. 调用实例上的特定方法;
  3. 销毁实例。 ```javascript let str = “some text”; str.substring(2);

// 其实真正的过程是 let str = “some text”; let s1 = new String(str); s1.substring(2); s1 = null; // 因为系统没有地方保存,所以立马执行 delete

  1. ```javascript
  2. var str2 = "zlq";
  3. str2.age = 18;
  4. console.log(str2.age); //undefined
  5. // 第二行代码运行时会临时创建一个 String 对象,
  6. // 当第三行代码执行时,这个对象已经被销毁了。
  7. // 实际上,第三行代码在这里创建了自己的 String 对象,但这个对象没有 age 属性。

这种行为可以让原始值拥有对象的行为。对BooleanNumber而言,以上 3 步也会在后台发生,只不过使用的是new Boolean()new Number()包装类型而已。
undefindnull不可以设置任何方法和属性。

  1. 可见,并非string调用了自身的方法,而是后台创建了一个基本包装类型String,从而进行下一步操作。
  2. 基本类型的“销毁性”致使我们不能为基本类型添加属性和方法。

new Number()构造函数实例化出的对象就变成了一个数值类型的对象

  1. var c = new Number(1);
  2. c.len = 1;
  3. console.log(c); // Number {1, len: 1}
  4. // 参数运算的时候,包装对象又会转换为原始值
  5. console.log(c + 1); // 2

面试题

看几道关于包装类的面试题:
type.text打印什么?

  1. var name = "languiji";
  2. name += 10; // languiji10
  3. var type = typeof name; // "string" new String(typeof name) 可以保存 text 属性
  4. if (type.length === 6) { // true
  5. // new String(type).text = "strign"
  6. // delete type
  7. type.text = "strign";
  8. }
  9. console.log(type.text); // undefind

打印的结果是什么?

  1. function Test(a, b, c) {
  2. var d = 1;
  3. this.a = a;
  4. this.b = b;
  5. this.c = c;
  6. function f() {
  7. d++;
  8. console.log(d);
  9. }
  10. this.g = f;
  11. // return this
  12. // 形成闭包
  13. }
  14. var test1 = new Test();
  15. test1.g(); // 2
  16. test1.g(); // 3
  17. var test2 = new Test()
  18. test2.g() // 2

问打印的结果?

  1. var x = 1,
  2. y = z = 0;
  3. function add(n) {
  4. return (n = n + 1);
  5. }
  6. y = add(x);
  7. function add(n) {
  8. return (n = n + 3);
  9. }
  10. z = add(x);
  11. console.log(x, y, z); // x=1 y=4 z=4
  12. // 解析
  13. /*
  14. 1、创建 AO 对象
  15. AO = {
  16. x: undefind
  17. y: undefind
  18. z: undefind
  19. add: function(n){return (n = n + 1);}
  20. 2、函数同名被覆盖
  21. add: function(n){return (n = n + 1);} =〉function(n){return (n = n + 3);}
  22. }
  23. */

关于函数的面试题:
问打印结果?

  1. // 因为 Car 内部没有赋值形参数
  2. function Car(brand, color) {
  3. this.brand = "Benz";
  4. this.color = "red";
  5. }
  6. var car = new Car("Mazda", "blank");
  7. console.log(car); // Benz red

问那个函数能打印出[1, 2, 3, 4, 5]?

  1. function foo1(x) {
  2. console.log(arguments); // [1,2,3,4,5]
  3. return x;
  4. }
  5. foo1(1, 2, 3, 4, 5);
  6. // 不会执行,因为 JS 引擎会将 function foo2 和 (1, 2, 3, 4, 5) 拆分为两个语句
  7. function foo2(x) {
  8. console.log(arguments);
  9. return x;
  10. }(1, 2, 3, 4, 5);
  11. (function foo3(x) {
  12. console.log(arguments); // [1,2,3,4,5]
  13. return x;
  14. })(1, 2, 3, 4, 5);

问打印结果?

  1. function b(x, y, a) {
  2. a = 10;
  3. console.log(arguments[2]); // 10
  4. }
  5. b(1, 2, 3);