我们在学习「预编译」的时候知道函数执行this存在,AO保存了this对象,this指向window
function Car(color) {// window.colorthis.color = color;}Car();
this在没有实例化的时候,它的指向是window,一旦实例化了构造函数以后,this指向实例化对象。**new**的作用就是创建**this**对象然后把**this**返回指向实例化对象。
// 不实例化对象的时候 this 指向 windowfunction Car(color, brand) {this.color = color;this.brand = brand;}var car1 = new Car("red", "Benz");var car2 = new Car("black", "Mazda");console.log(car1.color, car2.color); // red black
car1和car2都用的是this,但是数据不一样,证明this并不是指向window,而指向实例化对象。
构造函数的原理
先看一段代码:
function Car(color, brand) {this.color = color;this.brand = brand;}var car1 = new Car("red", "Benz");console.log(car1.color);/*** 代码执行先创建 GO* GO = {* Car: function,* car1: undefind* }** new Car() 的时候就相当于函数执行,函数执行就会创建 AO* AO = {** }** 因为 new 关键字的存在,Car() 就会变成构造函数,构造函数的 AO 默认创建 this 对象* new 关键字创建了 this 对象,最后 return this* AO = {* this:{}* }** 开始执行构造函数,构造函数内 this 赋值* AO = {* this:{* color: color* brand: brand* }* }** 赋值完成后 return this 对象,赋值给 car1* GO = {* Car: function,* car1: this* }** 所以可以访问 car1.color**/
我们知道了「构造函数」的执行过程能不能自己模拟一个new的过程呢?
function Car(color, brand) {var me = {};me.color = color;me.brand = brand;return me;}var car1 = Car("red", "Benz");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
<a name="a52sM"></a>## 包装类`JavaScript`有三个特殊的「原始类型」,分别是`String`、`Number`、`Boolean`我们都知道「引用类型」本身是有属性和方法的,比如:```javascriptvar arr = [1, 2, 3];arr.push(4);arr.length;
但是如String这样的原始类型为什么也会有length、subString()等属性和方法呢?
其实这是因为每当String调用方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的各种方法。
let s1 = "some text";let s2 = s1.substring(2);
包装类型对象的过程分为 3 步:
- 创建一个 String 类型的实例;
- 调用实例上的特定方法;
- 销毁实例。 ```javascript let str = “some text”; str.substring(2);
// 其实真正的过程是 let str = “some text”; let s1 = new String(str); s1.substring(2); s1 = null; // 因为系统没有地方保存,所以立马执行 delete
```javascriptvar str2 = "zlq";str2.age = 18;console.log(str2.age); //undefined// 第二行代码运行时会临时创建一个 String 对象,// 当第三行代码执行时,这个对象已经被销毁了。// 实际上,第三行代码在这里创建了自己的 String 对象,但这个对象没有 age 属性。
这种行为可以让原始值拥有对象的行为。对Boolean和Number而言,以上 3 步也会在后台发生,只不过使用的是new Boolean()和new Number()包装类型而已。undefind和null不可以设置任何方法和属性。
- 可见,并非
string调用了自身的方法,而是后台创建了一个基本包装类型String,从而进行下一步操作。- 基本类型的“销毁性”致使我们不能为基本类型添加属性和方法。
new Number()构造函数实例化出的对象就变成了一个数值类型的对象
var c = new Number(1);c.len = 1;console.log(c); // Number {1, len: 1}// 参数运算的时候,包装对象又会转换为原始值console.log(c + 1); // 2
面试题
看几道关于包装类的面试题:
问type.text打印什么?
var name = "languiji";name += 10; // languiji10var type = typeof name; // "string" new String(typeof name) 可以保存 text 属性if (type.length === 6) { // true// new String(type).text = "strign"// delete typetype.text = "strign";}console.log(type.text); // undefind
打印的结果是什么?
function Test(a, b, c) {var d = 1;this.a = a;this.b = b;this.c = c;function f() {d++;console.log(d);}this.g = f;// return this// 形成闭包}var test1 = new Test();test1.g(); // 2test1.g(); // 3var test2 = new Test()test2.g() // 2
问打印的结果?
var x = 1,y = z = 0;function add(n) {return (n = n + 1);}y = add(x);function add(n) {return (n = n + 3);}z = add(x);console.log(x, y, z); // x=1 y=4 z=4// 解析/*1、创建 AO 对象AO = {x: undefindy: undefindz: undefindadd: function(n){return (n = n + 1);}2、函数同名被覆盖add: function(n){return (n = n + 1);} =〉function(n){return (n = n + 3);}}*/
关于函数的面试题:
问打印结果?
// 因为 Car 内部没有赋值形参数function Car(brand, color) {this.brand = "Benz";this.color = "red";}var car = new Car("Mazda", "blank");console.log(car); // Benz red
问那个函数能打印出[1, 2, 3, 4, 5]?
function foo1(x) {console.log(arguments); // [1,2,3,4,5]return x;}foo1(1, 2, 3, 4, 5);// 不会执行,因为 JS 引擎会将 function foo2 和 (1, 2, 3, 4, 5) 拆分为两个语句function foo2(x) {console.log(arguments);return x;}(1, 2, 3, 4, 5);(function foo3(x) {console.log(arguments); // [1,2,3,4,5]return x;})(1, 2, 3, 4, 5);
问打印结果?
function b(x, y, a) {a = 10;console.log(arguments[2]); // 10}b(1, 2, 3);
