我们在学习「预编译」的时候知道函数执行this
存在,AO
保存了this
对象,this
指向window
function Car(color) {
// window.color
this.color = color;
}
Car();
this
在没有实例化的时候,它的指向是window
,一旦实例化了构造函数以后,this
指向实例化对象。**new**
的作用就是创建**this**
对象然后把**this**
返回指向实例化对象。
// 不实例化对象的时候 this 指向 window
function 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`
我们都知道「引用类型」本身是有属性和方法的,比如:
```javascript
var 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
```javascript
var 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; // languiji10
var type = typeof name; // "string" new String(typeof name) 可以保存 text 属性
if (type.length === 6) { // true
// new String(type).text = "strign"
// delete type
type.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(); // 2
test1.g(); // 3
var 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: undefind
y: undefind
z: undefind
add: 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);