【前端丛林】JavaScript这样服用,效果更佳(4)

354 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

前言

哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是我们冒险的第四天,经过三天的考验相信大家已经有了足够的经验,今天我们会遇到一个相对难对付的猎物,大家做好准备!话不多说,现在开启我们今天的前端丛林冒险之旅吧!

1. 面向对象

  • 对象为无序属性的集合,其属性可以包含基本值、对象和函数

1.1 原型链

  • JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

1.1.1 一切皆对象

  • 对象就是一些属性的集合
  • 方法也是一种属性
  • 一切(引用类型)都是对象,对象是属性的集合
  • 函数和数组也是对象

1.1.1.1 typeof

  • 检测数据类型 typeof 返回的都是字符串
  • 基本数据类型 number string boolean undefined symbol
  • 引用类型 null {} [] /&$/ Date => object
console.log(typeof a); // undefined
console.log(typeof 1); // number
console.log(typeof 'xiaoming'); // string
console.log(typeof true); // boolean
console.log(typeof Symbol('a')); // symbol
console.log(typeof function () { }); //function
console.log(typeof [1, 2, 3]); //object
console.log(typeof { name: 'xiaoming' }); //object
console.log(typeof null); //object
console.log(typeof new Number(1)); //object

1.1.2 函数

  • 对象是通过函数创建的
  • 批量生产对象的函数 Object
  • 实现私有和公有属性的封装
let obj = new Object();
obj.name='xiaoming';
obj.age = 18;

1.1.3 隐式原型

1.1.3.1 proto

  • 每个对象都有一个proto属性,指向创建该对象的函数的prototype
  • Object.prototype proto 指向的是 null

image.png

image.png

1.1.3.2 自定义函数的prototype

  • 自定义函数的 prototype 的proto指向的就是Object.prototype

image.png

1.1.3.3 自定义函数

  • 自定义函数Foo.proto指向Function.prototype
  • Function的prototype和proto都指向 Function.prototype
let add = new Function('a','b','return a+b');
console.log(add(1,2));

image.png

image.png

1.1.4 instanceof

  • instanceof运算符的第一个参数是一个对象,第二个参数一般是一个函数
  • instanceof的判断规则是: 沿着对象的 proto 这条链来向上查找找,如果能找到函数的prototype则返回true,否则 返回false

image.png

1.2 批量创建对象

  • 通过 new 来调用一个函数,这个函数就成为了构造函数,构造函数里可以对例对象的私有属性赋值
  • 每个函数会有一个 prototype 属性,此原型对象上存放所有实例的公有方法
  • 若new的构造函数自己返回引用值,则以自己返回的为主,否则 返回创建的实例
  • create
function Person(name) {
    this.name = name;
}
Person.prototype.getName = function () {
      console.log(this.name);
}
let person = new Person('Lotzinfly');
person.getName();
Object.create = function (proto) {
    function F() {}
    F.prototype = proto;
    return new F();
};
function _new(clazz, ...args) {
    let _this = Object.create(clazz.prototype);
    let result = clazz.call(_this, ...args);
    if ((result != null && typeof result === 'object') || typeof result === 'function')
  {
      return result;
    }
   return _this;
}
  • 在使用原型继承编写复杂代码之前,理解原型继承模型是至关重要的。此外,请注意代码中原型链的长度,并在必要时将其分解,以避免可能的性能问题。此外,原生原型不应该被扩展,除非它是为了与新的 JavaScript 特性兼容。

1.3 继承

class Father {
      static staticFatherName = "FatherName"
      static staticGetFatherName = function () {
           console.log(Father.staticFatherName);
      }
      constructor(public name) {
       this.name = name;
  }
      getName() {
          console.log(this.name);
    }
}
class Child extends Father {
      static staticChildName = "ChildName"
      static staticGetChildName = function () {
      console.log(Child.staticChildName);
}
      constructor(public name, public age) {
         super(name);
         this.age = age;
   }
      getAge() {
        console.log(this.age);
  }
}
let child = new Child('xiaoming', 10);
child.getName();
child.getAge();
Child.staticGetFatherName();
Child.staticGetChildName();
var _extends = (function () {
    var extendStatics = function (Child, Father) {
    return Object.setPrototypeOf(Child, Father);
    }
    return function (Child, Father) {
       extendStatics(Child, Father);
       function Temp() {
         this.constructor = Child;
      }
    Temp.prototype = Father.prototype;
    Child.prototype = new Temp();
  };
})();
var Father = (function () {
    function Father(name) { this.name = name;
   }
    Father.prototype.getName = function () {
    console.log(this.name);
};
Father.staticFatherName = "FatherName";
Father.staticGetFatherName = function () {
   console.log(Father.staticFatherName);
};
  return Father;
}());
//_super父类构造函数
var Child = (function (_super) {
   _extends(Child, _super);
  function Child(name, age) {
  _super.call(this, name);//继承父类的实例私有属性
  this.age = age;
  return this;
 }
  Child.prototype.getAge = function () {
     console.log(this.age);
 };
Child.staticChildName = "ChildName";
Child.staticGetChildName = function () {
    console.log(Child.staticChildName);
};
  return Child;
}(Father));
let child = new Child('xiaoming', 10);
console.log(child);
child.getName();
child.getAge();
Child.staticGetFatherName();
Child.staticGetChildName();

1.3.1 原型链面试题

function Foo() {
   getName = function () {
   console.log(1);
}
   return this;
}
Foo.getName = function () {
    console.log(2);
}
Foo.prototype.getName = function () {
    console.log(3);
}
var getName = function () {
    console.log(4);
}
function getName() {
    console.log(5);
}
Foo.getName();
getName();
Foo().getName();
getName();//1
new Foo.getName();
new Foo().getName();
new new Foo().getName();
// 2 4 1 1 2 3 3

1.3.2 异步面试题

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2() {
    console.log('async2')
}
console.log('script start')
setTimeout(function () {
     console.log('setTimeout')
})
async1()
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
   console.log('promise2')
})
console.log('script end')
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

2.一张图理解原型与原型链之间的关系

image.png

结尾

好啦,这期的前端丛林大冒险先到这里啦!这期的猎物主要是原型与原型链,原型与原型链是一个不好对付猎物,大家一定要多打几遍,把它搞定,啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将,我们下期再见。拜拜!