原生JS知识

114 阅读3分钟

原型和原型链

1.1. 先上张图

avatar

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
```

手写一个Promise