原型和原型链
1.1. 先上张图
1.2 创建对象的方法
//1.字面量
var obj1 = {name: 'solo obj1'};
//2.new Object
var obj2 = new Object({name: 'solo obj2'})
//3.构造函数创建
var M = function(name){
this.name = name;
}
var obj3 = new M('solo obj3');
//4.Object.create
var p = {name: 'p'};
var obj4 = Object.create(p);
- js中的对象都是由构造函数创造出来的(对象字面量其实是一种语法糖, 本质也是由构造函数创造的),除了箭头函数,所有函数都存在一个叫prototype的属性,这个属性是一个指针,指向一个对象叫原型对象(包含了所有实例共享的属性和方法)
- 当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
const arr = [1, 2, 3]
arr.__proto__ === Array.prototype // true
Array.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // true
1.3. instanceof 的原理
instanceof 的原理是判断实例对象的 proto 是否与构造函数的 prototype 指向同一个引用。
typeof 对于基本数据类型(null, undefined, string, number, boolean, symbol),除了 null 都会返回正确的类型。null 会返回 object。
typeof 对于对象类型,除了函数会返回 function,其他的都返回 object。
如果我们想获得一个变量的正确类型,可以通过 Object.prototype.toString.call(xx)。这样我们就可以获得类似 [object Type] 的字符串。
1.4. 小手写一个new运算符
function Person(name, age){
this.name = name;
this.age = age;
}
//手动实现new
function _new(){
//1.拿到传入的参数中的第一个参数,即构造函数名Func
var Func = [].shift.call(arguments)
//2.创建一个空对象obj,并让其继承Func.prototype
var obj = Object.create(Func.prototype)
//第二步也可以这样写
// var obj = {}
// obj.__proto__ = Func.prototype
//3.执行构造函数,并将this指向创建的空对象obj
var ret = Func.apply(obj, arguments)
//4.如果构造函数返回的值是对象则返回,不是对象则返回创建的对象obj
return typeof ret === 'object' ? ret : obj
}
var p1 = _new(Person, 'bob', 19)
console.log(p1)
面向对象
1.1 类的声明和实例化
声明类有两种方式
function Animal(name){
this.name = name;
}
class Animal2 {
constructor(name){
this.name = name;
}
}
类的实例化只有一种方式
var a1 = new Animal('shape');
var a2 = new Animal2('cat');
1.2. 继承
1.2.1 构造函数继承
```
function Parent (name) {
this.name = name
}
Parent.prototype.say = function () {
console.log('Parent say hi')
}
function Child (name, age) {
Parent.call(this, name) //重点在这里
this.age = age
}
const child = new Child('zg', 18)
console.log(child) // Child {name: "zg", age: 18}
child.say() // 报错,child.say is not a function
总结:只能继承在父类显式声明的属性,不能继承父类原型链上的属性。
```
1.2.2 原型链继承
```
function Parent1 (name) {
this.name = name
this.arr = [1, 2, 3]
}
function Child1 (age) {
this.age = age
}
// 重点在这句
Child1.prototype = new Parent1('bobo')
const c2 = new Child1(12)
const c3 = new Child1(12)
c2.arr.push(4)
console.log(c2, c3)
总结:实例对象c2,c3的arr指向同一个引用,改变一个值,另外一个也会改变
```
1.2.3 组合式继承
```
function Parent2 (name) {
this.name = name
this.arr = [1, 2, 3, 4]
}
function Child2 (name, age) {
Parent2.call(this, name)
this.age = age
}
// 继承组合第一种方式
// Child2.prototype = new Parent2()
// 当Child2实例化的时候,Parent2构造函数被执行两次,没有必要
// 继承组合第二种方式
Child2.prototype = Parent2.prototype
const c4 = new Child2('zg', 13)
const c5 = new Child2('zg', 13)
console.log(c4 instanceof Child2, c5 instanceof Child2) // true
console.log(c4.constructor) // Parent2
// 应该是Child2, 但是打印出来是Parent2
// 因为 Child2.prototype = Parent2.prototype ,共用了一个原型对象,所以 Child2.prototype.constructor 当然也是 Parent2 了。
// 继承组合第三种方式
Child2.prototype = Object.create(Parent2.prototype) // Object.creeate(),Child2.prototype和Parent2.prototype不再使用同一个引用,中间用一个新对象隔开
Child2.prototype.constructor = Child2 // 此时Child2没有constructor, 手动指定一个
var c6 = new Child2()
console.log(c6 instanceof Child2, c6 instanceof Parent2) // true
console.log(c6.constructor) // Child2
```