这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
一、创建对象的方式:实例 & 字面量
1. 字面量创建数据和实例的方式创建数据
1.1 实例的创建方式—— new 操作符
实例的创建方式,通过 new 操作符调用当前类型的构造函数,会得到当前类型的一个实例
- 创建引用数据类型:
var ary2 = new Array(1, 2, 3, 4); // 用实例的方式创建一个数组
ary2.push(5);
var ary = [1, 2, 3, 4]; // 字面量创建数组
ary.push(5);
- 创建引用基本类型
var str1 = new String('javascript');
console.log(str1.toUpperCase());
var str2 = 'javascript';
console.log(str2.toUpperCase());
console.log(typeof str1); // 'object'
console.log(typeof str2); // 'string'
基本数据类型的只能通过字面量的方式创建,如果用实例的方式创建,这个实例将会是变成一个对象,而对象不是基本数据类型;
1.2 字面量创建方式
- 创建引用数据类型:字面量创建的引用该数据类型和实例的方式创建的引用数据类型没有区别
// 1. 对象:{}
var obj = {};
// 2. 数组:[]
var ary = [1, 2, 4];
// 3. 正则 /^\d$/
var reg = /^\d$/;
- 创建基本数据类型:
var num = 1;
var str = 'abc';
var bool = true;
var empty = null;
var notDefined = undefined;
var sym = Symbol('cbd');
二、 new 调用构造函数和普通调用的区别
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道受业解惑';
this.age = age;
this.subject = subject;
this.from = from;
this.teach = function () {
console.log(`${name} 老师教 ${subject} 学科`);
};
// 上面这种通过this.xxx = xxx的方式直接向实例上添加的属性成为私有属性。
var career = '前端'; // 这只是一个私有变量,不会和实例产生联系
}
// 普通函数执行:
let t1 = Teacher('和雍', 18, 'js', '江外');
console.log(t1); // undefined
let t2 = new Teacher('陆辉', 19, 'js', '江外');
console.log(t2); // Teacher {naem:......}
- 为啥会出现这种情况呢?这是因为 new 调用和普通调用有着本质的区别;
2.1 函数的普通调用过程:
- 新开辟栈内存作为执行的作用域
- 形参赋值
- 私有作用域变量提升
- 代码从上到下执行
- 释放栈内存
2.2 new 调用时:
- 开辟栈内存
- 形参赋值
- 变量提升
- 隐式创建一个实例对象,并且把当前构造函数中的this指向这个实例对象。
- 执行函数体中的代码,当遇到this.xxx = xxx时就是在向实例对象上增加属性;
- 隐式返回实例对象
- 释放栈内存
3. 显式设置返回值
3.1 返回基本数据类型:
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道受业解惑';
this.age = age;
this.subject = subject;
this.from = from;
this.teach = function () {
console.log(`${name} 老师教 ${subject} 学科`);
};
return 61; // 返回基本类型值
}
let t1 = new Teacher('和雍', 18, 'js', 'hy');
console.log(t1); // Teacher {....}
3.2 返回引用数据类型
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道受业解惑';
this.age = age;
this.subject = subject;
this.from = from;
this.teach = function () {
console.log(`${name} 老师教 ${subject} 学科`);
};
return {haha: '哈哈'}
}
let t2 = new Teacher('陆辉', 19, 'js', '江外');
console.log(t2); // {haha: ...}
如果我们手动修改构造函数的返回值时:
- 如果return一个
基本数据类型的值,没有任何影响,不会覆盖原有实例; - 如果return
引用数据类型,原有的实例就会被这个引用类型值覆盖
慎重修改构造函数的返回值
三、 基于原型的原型模式模式
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道受业解惑';
this.age = age;
this.subject = subject;
this.from = from;
this.teach = function () {
console.log(`${name} 老师教 ${subject} 学科`);
};
}
// let t1 = new Teacher('mabin', 18, 'js', 'hy');
// let t2 = new Teacher('mabin', 19, 'FE', 'hy');
//
// t1.teach();
// t2.teach();
// console.log(t1.teach === t2.teach); // false 因为t1和t2是两个实例,而teach又是t1和t2的私有方法
但是数组的push方法呢?
var ary1 = [1, 2, 3];
var ary2 = [1, 3, 5];
console.log(ary1.push === ary2.push); // true
console.log(ary1);
console.log(ary2);
思考? 只要是数组就该能push,因为这是数组类的功能,所以。而老师是Teacher类的实例,只要是老师就能teach,teach是老师类的功能,不需要再每个实例上强调一遍。这是一个公有的特性,不该放在实例,而是应该放在一个公共的地方,以后只要是这个类的实例就有这个功能。放在哪里呢?答案就是原型对象
3.1 原型对象
原型prototype:每一个函数(普通函数、构造函数【类】)都天生自带一个属性prototype(原型)。这个属性的值是一个对象,用来存储当前类型的共有的属性和方法。保存在原型上面的属性和方法称为公有属性或公有方法。
所以改造Teacher类型:
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道受业解惑';
this.age = age;
this.subject = subject;
this.from = from;
}
Teacher.prototype.teach = function () {
console.log(`${this.name} 老师教 ${this.subject} 学科`);
};
console.log(Teacher.prototype);
let t1 = new Teacher('mabin', 18, 'js', 'hy');
let t2 = new Teacher('jiangwen', 19, 'FE', 'hy');
t1.teach();
t2.teach();
console.log(t1.teach === t2.teach); // true
四、 公有属性和私有属性
4.1 定义
公有属性即某个类型都可以访问到的属性,是所有这个类型的实例都拥有的;而私有属性则是某个实例独有的,有别与公有属性,只有实例自身可以访问到;
举个例子,你我都是中国人,这是类,有别于外国人,比如祖国就是个公有属性,只要是中国人,他的祖国就是中国(汉奸、精分、和杠精除外);而存款就是私有属性了,你的存款只有你能使用他,我则不能使用,如果我一定要使用,就得去享受一下公费再教育!
4.2 如何检测属性是公有还是私有?
hasOwnProperty() 方法:检测某个属性是否是对象的私有属性,如果是私有属性,返回true,否则返回false;
var r1 = t1.hasOwnProperty('name'); // true
var r2 = t2.hasOwnProperty('teach'); // false
五、 原型 & 原型链
5.1 原型对象
JavaScript 是通过原型实现的面向对象,而原型是类的一个特有属性,即 prototype 属性,通过 类.prototyep 访问,通过原型链也可以访问!
function Teacher(name, age, subject, from) {
this.name = name;
this.mission = '传道受业解惑';
this.age = age;
this.subject = subject;
this.from = from;
}
Teacher.prototype.teach = function () {
console.log(`${this.name} 老师教 ${this.subject} 学科`);
};
console.log(Teacher.prototype);
let t1 = new Teacher('mabin', 18, 'js', 'hy');
let t2 = new Teacher('jiangwen', 19, 'FE', 'hy');
t1.teach();
t2.teach();
思考? 我把这个方法写在了 Teacher 的原型上,那么 t1 和 t2 是怎么找到的呢?
console.log(t1);
console.log(t2);
console.log(Teacher.prototype);
在私有属性中没有发现teach方法。我们发现这个t1和t2中有一个__proto__ 属性,这个属性值里面有teach方法。那是怎么找过去呢?Teacher.prototype中也有一个__proto__,这是干嘛呢的?
5.2 原型链
什么是原型链,作用是什么?
原型链是对象的属性查找机制!
每个实例都有一个属性__proto__ 属性,它指向当前实例所属类的 prototype 对象。
当我们访对象的一个的属性时,如果有,就使用私有属性,如果没有就通过实例 __proto__ 找到实例所属类的 prototype (原型)上查找,如果找到就使用 prototype 上的属性,如果还没找到,就通过 prototype 的 __proto__ 继续向上查找,一直找到 Object 的prototype 就停止查找。
如果还没找到就返回undefined。