一、构造函数

1-1 实例成员、静态成员

  • 实例成员:实例成员就是在构造函数内部,通过this添加的成员。实例成员只能通过实例化的对象来访问
  • 静态成员:在构造函数本身上添加的成员,只能通过构造函数来访问 ```javascript function Person(name,age) {

    1. //实例成员
    2. this.name = name;
    3. this.age = age;

    } //静态成员 Star.sex = ‘女’;

    let stars = new Person(‘小红’,18); console.log(stars); // Star {name: “小红”, age: 18} console.log(stars.sex); // undefined

    1. console.log(Star.sex); // '女'
  1. <a name="bEc8Y"></a>
  2. #### 1-2 定义方法位置对比
  3. - 在构造函数上直接定义方法(不共享)
  4. ```javascript
  5. function Star() {
  6. this.sing = function () {
  7. console.log('我爱唱歌');
  8. }
  9. }
  10. let stu1 = new Star();
  11. let stu2 = new Star();
  12. stu1.sing();//我爱唱歌
  13. stu2.sing();//我爱唱歌
  14. console.log(stu1.sing === stu2.sing); //false stu1 和 stu2 指向的不是一个地方。

缺点:如果方法在构造函数内部,每次生成实例,都会新开辟一个内存空间存方法。这样会导致内存的极大浪费,从而影响性能。

  • 通过原型添加方法(共享)
    1. function Star(name) {
    2. this.name = name;
    3. }
    4. Star.prototype.sing = function () {
    5. console.log('我爱唱歌', this.name);
    6. };
    7. let stu1 = new Star('小红');
    8. let stu2 = new Star('小蓝');
    9. stu1.sing();//我爱唱歌 小红
    10. stu2.sing();//我爱唱歌 小蓝
    11. console.log(stu1.sing === stu2.sing);//true

    1-3 构造函数的原型链继承(常用)

    1. function Animal() {
    2. this.eat = function() {
    3. console.log('animal eat')
    4. }
    5. }
    6. function Dog() {
    7. this.break = function() {
    8. console.log('dog bark');
    9. }
    10. }
    11. // Dog.prototype的__proto__指向Animal.prototype
    12. Dog.prototype = new Animal();
    13. var hashiqi = new Dog();
    原始继承.png

    1-4 构造函数的特点

  1. 首字母必须为大写,用来区分普通函数
  2. 内部使用的 this 对象,来指向即将要生成的实例对象
  3. 使用 new 关键字来生成实例对象

    二、class

    2-1 类的定义

    基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已 ```javascript // 构造函数和class的对比

// ES5 function Point(x, y) { this.x = x; this.y = y; }

Point.prototype.toString = function () { return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)’; };

var p = new Point(1, 2);

// ES6 class Point { constructor(x, y) { this.x = x; this.y = y; }

toString() { return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)’} // 等价于 // Point.prototype.toString = function () { // return ‘(‘ + this.x + ‘, ‘ + this.y + ‘)’; // }; static toString = function(){} // 等价于 // Point.toString = function(){} }

  1. <a name="WXQk9"></a>
  2. #### 2-2 类的特点
  3. 1. class中的 constructor 函数相当于 ES5 中的构造函数
  4. 1. class中定义方法是定义在类的 prototype 属性
  5. 1. class的内部定义的所有方法都是不可枚举的
  6. 1. class和模块内部默认采用严格模式
  7. 1. 子类继承父类以后,必须在 constructor 中调用时 super 方法,否则不能新建实例,因为子类没有属于自己的this对象,而是继承了父类的this对象对其进行加工
  8. <a name="uLmNE"></a>
  9. # 三、原型
  10. <a name="fQjQA"></a>
  11. #### 3-1 基本概念
  12. - 每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性,比如 [ ] 的 原型就是 Array.prototype
  13. - 原型的作用就是共享方法,不用反复开辟内存
  14. - 原型的 constructor 属性指向其构造函数,如果替换了原型对象之后,这个 constructor 属性就不准确,需要手动补充一下
  15. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12508812/1640226365660-c9061cb3-2011-4695-892c-380566eb8110.png#clientId=u586ce051-074d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=367&id=ua33c60cc&margin=%5Bobject%20Object%5D&name=image.png&originHeight=734&originWidth=1277&originalType=binary&ratio=1&rotation=0&showTitle=false&size=100352&status=done&style=none&taskId=u1b3ccabc-62a7-4a1e-9473-26d86e57cd4&title=&width=638.5)
  16. <a name="nNpLM"></a>
  17. #### 3-2 原型规则
  18. - 所有引用类型(对象、数组、方法),除了 null ,都有一个隐式原型 __proto__ 属性,属性值是一个普通的对象,指向其构造函数的显式原型 prototype ,也就是指向该引用类型的原型
  19. - 当访问一个对象的属性时,先在基本属性中查找,如果没有,那么它会去它的隐式原型 __proto__(也就是它的构造函数的显式原型 prototype )中寻找
  20. - 所有的函数,都具有一个显式原型 prototype ,属性值也是一个普通对象
  21. ```javascript
  22. function Person() {
  23. }
  24. var person = new Person();
  25. console.log(person.__proto__ == Person.prototype) // true
  26. console.log(Person.prototype.constructor == Person) // true
  27. // 顺便学习一个ES5的方法,可以获得对象的原型
  28. console.log(Object.getPrototypeOf(person) === Person.prototype) // true

3-3 constructor

  1. function Person() { }
  2. var person = new Person();
  3. console.log(person.constructor === Person); // true

众所周知,原型的 constructor 属性指向其构造函数,当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到 constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性

3-4 proto

绝大部分浏览器都支持使用 proto 这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)

四、原型链

截图.png

  • 原型与原型层层相链接的过程即为原型链
  • Object.prototype的原型 为 null 跟 Object.prototype 没有原型表达的是一个意思

    4-1 原型链例子

    1. // 父类
    2. class People {
    3. constructor(name) {
    4. this.name = name
    5. }
    6. eat() {
    7. console.log(`${this.name} eat something`)
    8. }
    9. }
    10. // 子类
    11. class Student extends People {
    12. constructor(name, number) {
    13. super(name)
    14. this.number = number
    15. }
    16. sayHi() {
    17. console.log(`姓名 ${this.name} 学号 ${this.number}`)
    18. }
    19. }
    20. // 子类
    21. class Teacher extends People {
    22. constructor(name, major) {
    23. super(name)
    24. this.major = major
    25. }
    26. teach() {
    27. console.log(`${this.name} 教授 ${this.major}`)
    28. }
    29. }
    30. // 实例
    31. const xialuo = new Student('夏洛', 100)
    32. console.log(xialuo instanceof Student) //true
    33. console.log(xialuo instanceof People) //true ,People是Student的父类,也参与了xialuo的构造
    34. console.log(xialuo instanceof Object) //true ,Object是所有类的父类
    35. console.log([] instanceof Array) //true
    36. console.log([] instanceof Object) //true
    37. //instanceof 运算符用于检测构造函数的 prototype 属性是否出现在实例对象的原型链上。

    4-2 Object.proto

  • 定义1:访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着 proto 这条链向上找,这就是原型链。

  • 所以 Object.toString 是会报错的 ,Object 找 toString 方法的时候会沿着 proto 这条链上找,因为Object.proto上没有toString方法,所以报错,必须使用 Object.prototype.toString
  • 定义2:对象的 proto 指向其构造函数的 prototype ,所以 Object._proto 指向 Function.prototype
    1. Object.__proto__ === Function.prototype // true