(一) js面向对象编程(oop)

  1. 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
  2. 面向对象编程三大要素: 封装、多态、继承

(二) 面向对象编程之封装

  1. 封装: 就是把属性和方法都封装到一个对象里
  2. 创建对象的几种方式

    1. (1)字面量的方式
    2. (2)构造函数的方式
    3. (3)自定义对象的方式
    4. (4)工厂模式
    5. (5)原型链模式
    1. <script>
    2. // 1.字面量方式创建对象
    3. var obj1 = {
    4. a: 2,
    5. b: 3
    6. };
    7. // 2.使用构造函数创建对象
    8. var obj = new Object();
    9. obj.name = '张三';
    10. obj.sayName = function() {
    11. console.log(this.name);
    12. }
    13. // 3.自定义构造函数
    14. function CreateGf(name, height, sex, age) {
    15. this.name = name;
    16. this.height = height;
    17. this.sex = sex;
    18. this.age = age;
    19. this.sayName = function() {
    20. console.log('我叫' + this.name);
    21. }
    22. }
    23. var gf = new CreateGf('王冰冰', 168, '女', 18);
    24. console.log('gf', gf);
    25. // var gf2 = new CreateGf('李冰冰', 168, '女', 18);
    26. // console.log('gf2', gf2);
    27. // 4.工厂模式(批量制造)创建对象,相对比2的方法,创建2个对象时,sayName方法不需要写两遍
    28. function madeCat(name, age) {
    29. var obj = new Object();
    30. obj.name = name;
    31. obj.age = age;
    32. obj.sayName = function() {
    33. console.log(obj.name);
    34. }
    35. return obj;
    36. }
    37. var cat1 = madeCat('小白', 2);
    38. var cat2 = madeCat('小花', 3);
    39. // 5. 原型模式创造对象,详看继承一节
    40. </script>
  3. 判断对象归属(亲子鉴定)

(三) 面向对象编程之多态

多态:允许将子类类型的指针赋值给父类类型的指针;原生JS是弱类型语言,没有多态概念.

(四) 面向对象编程之继承

(1)原型

  1. 每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针
    名词解释: 指针后台语言说法, 在前端就是属性的意思

    1. <script>
    2. // 1. 每个构造函数都有一个prototype属性,指向了它的原型对象(打开控制台查看试试)
    3. // 2. 原型对象都存在一个属性constructor指向了构造函数,例如Date构造函数:
    4. console.log(Array.prototype);
    5. </script>
  2. 每个实例都包含一个指向原型 对象的内部指针
    名词解释: 实例就是构造函数new出来的对象

    1. <script>
    2. // a.每个实例都存在一个属性__proto__, 指向了原型对象
    3. var date = new Date();
    4. var arr = new Array(1, 2, 3);
    5. console.log(date.__proto__);
    6. console.log(arr.__proto__);
    7. // 根据第1点每个构造函数都有一个原型对象, 思考问题: 构造器的prototype和实例的__proto是什么关系
    8. console.log(Array.prototype === arr.__proto__);
    9. </script>
  3. 实例继承原型对象的所有方法

    1. <script>
    2. // 1. 创建数组对象
    3. var arr = [1, 2, 3, 4];
    4. // 2. arr就会拥有length属性,push,pop等方法,请问它的属性和方法是它的原型对象给的
    5. console.log(arr.length);
    6. arr.push(5);
    7. console.log(arr);
    8. // 3. 自定义一个构造函数, 给它的构造器和原型对象添加属性或方法
    9. function MadeCat() {
    10. this.name = '猫';
    11. }
    12. MadeCat.prototype.color = '黑色';
    13. MadeCat.prototype.sayName = function() {
    14. console.log(this.name);
    15. }
    16. var cat = new MadeCat();
    17. console.log('cat', cat);
    18. </script>
  4. 构造函数, 原型对象,实例三者关系的比喻(看图)

实例-构造函数-原型对象关系图(单个).png

  1. 实例、构造函数、原型对象三者的关系就好像儿子、妈妈、爸爸的关系
  2. 1.每一个妻子(构造函数)都有一个丈夫(原型对象),丈夫(原型对象)身上有一个结婚证,结婚证上那个女人的名字就是他妻子(构造函数)
  3. 2.小孩是由妈妈生产出来的(就好像实例是由构造对象new出来一样),每个小孩(实例)都有一个爸爸(原型对象)
  4. 结论:
  5. 1.构造函数.prototype === 原型对象
  6. 2.实例.__proto__ === 原型对象
  7. 3.构造函数.prototype === 实例.__proto__; // 原型对象是同一个

(2)原型链

  1. 原型和实例的关系:

实例内容有一个指针指向了原型对象(换一种说法就是实例有一个属性指向了原型对象)

  1. // 创建日期实例(对象)
  2. var date = new Date();
  3. // 日期实例有一个属性__proto__指向了原型对象
  4. console.log('原型对象=',date.__proto__);

2. 原型链
(1) 实例、构造函数、原型对象关系图
实例-构造函数-原型对象关系图(多个).png结论: 根据上图, 存在以下关系:

  1. var arr = [1,2,3];
  2. var date = new Date();
  3. var obj = {name:'zs'};
  4. // 1.实例.__proto__===构造函数.prototype
  5. date.__proto__ === Date.prototype;
  6. arr.__proto__ === Array.prototype;
  7. cat.__proto__ === MadeCat.prototype;
  8. obj.__proto__ === Object.prototype;
  9. // 2.终极原型都一样, 都是Object的原型对象
  10. date.__proto__.__proto__ === Object.prototype;
  11. arr.__proto__.__proto__ === Object.prototype;
  12. cat.__proto__.__proto__ === Object.prototype;
  13. // 3. 根据第2点得到
  14. date.__proto__.__proto__ === arr.__proto__.__proto__;
  15. arr.__proto__.__proto__ === cat.__proto__.__proto__;
  16. date.__proto__.__proto__ === cat.__proto__.__proto__;
  1. (2) 原型链概念: 假如我们让原型对象等于另一个类型的实例, ,此时的原型对象将包含一个指向另一个原型对象的指针,假如另一个原型对象又是另一个类型的实例, 那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。 ```javascript // 举例说明 // 动物类型构造器 function MadeAnimal() { this.type = ‘动物’; } // 创建一个动物的实例 var animal = new MadeAnimal();

// 猫类型构造器 function MadeCat(name,age) { this.name = name; this.age = age; } // 让猫的原型对象等于动物类型的一个实例 MadeCat.prototype = animal; var cat = new MadeCat(‘小花猫’,2);

// 就会存在以下的关系: (1)猫的实例cat存在一个属性proto指向了猫的原型对象, (2)因为猫的原型对象是动物的一个实例animal,既然它是个实例,则animal实例就会存在一个proto属性,指向了动物的原型对象 (3)实际上,动物的原型对象还存在一个proto属性,它指向了Object的原型对象,所以以下的等式是成立的 cat.proto 猫的原型对象是animal cat.proto.proto 这是animal的原型对象 cat.proto.proto.proto animal原型对象内部还存在一个proto属性,指向了Object.prototype cat.proto.proto.proto === Object.prototype; //

  1. 问到原型和原型链这么回答:
  2. **原型:** 原型指的是原型对象, 每一个构造函数都会有一个对应的原型对象, 同时原型对象也存在一个指针(属性),指向了它的构造器.
  3. **原型链:**
  4. 1. 我们创建的每一个实例, 都存在一个内部指针(属性,叫`__proto__`), 指向了它的原型对象, 并且会继承原型对象的属性和方法
  5. 1. 假如我们让原型对象等于另一个类型的实例, ,此时的原型对象将包含一个指向另一个原型对象的指针,假如另一个原型对象又是另一个类型的实例, 那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。<br />ps: 2点其实是第1点的重复
  6. <a name="2d34890d"></a>
  7. ### (3)继承(需要掌握call,apply和bind的知识)
  8. js5.1版本常见的有5中继承方式: 原型继承, 借用构造函数继承, 组合继承, 寄生式继承, 寄生组合式继承
  9. 1. 原型继承: 让子类的原型对象=父类的实例
  10. 缺点:
  11. - 父类的引用类型属性会被所有子类实例共享,任何一个子类实例修改了父类的引用类型属性,其他子类实例都会受到影响
  12. - 创建子类实例的时候,不能向父类传参
  13. 2. 借用构造函数继承
  14. 在子类的构造函数中调用父类的构造器实现继承
  15. - 优点:
  16. - 避免了引用类型属性被所有实例共享
  17. - 可以向父类传参
  18. - 缺点:
  19. - 方法必须定义在构造函数中
  20. - 每创建一个实例都会创建一遍方法
  21. 3. 寄生式继承
  22. 需要先传入一个对象, 再往对象上添加属性或方法, 就好像寄生一样
  23. ```javascript
  24. function createObj(o) {
  25. var clone = object.create(o);
  26. clone.sayName = function () {
  27. console.log('hello');
  28. }
  29. return clone;
  30. }
  31. var father = {
  32. name: '人',
  33. nation: '中国'
  34. }
  35. var son = createObj(father);
  36. // son继承了father,拥有father的属性,同时还拥有sayName的方法
  37. console.log(son);
  1. 就是创建一个封装的函数来增强原有对象的属性,缺点跟借用构造函数一样,每个实例都会创建一遍方法
  2. 组合式继承(第1种和第2种组合)

既有原型继承,也借用了父类的构造函数

  1. function Father(name, age) {
  2. this.name = name;
  3. this.age = age;
  4. console.log(this);
  5. }
  6. Father.prototype.say = function() {
  7. console.log('hello');
  8. }
  9. function Child(name,age) {
  10. Father.call(this,name,age);
  11. }
  12. Child.prototype = new Father();
  13. var child = new Child('Tom', 22);
  14. console.log(child);
  1. 组合式继承方式唯一的缺点是,父类的构造函数会被调用两次
  2. 寄生组合式继承(最完美的继承方式) ```javascript var obj = {name: ‘zs’}; var obj2 = Object.create(obj); // obj和obj2是两个不同的对象,它们的关系是: obj2.proto === obj;相当于说obj2是obj的一个实例了, obj是obj2的原型对象了

// 原型+借用构造函数+寄生 function Person() { console.log(22); this.class = ‘人类’; }

// 原型继承模式 Person.prototype.say = function() { console.log(this.name); }

// 让Man的原型对象的原型对象 = Person的原型对象,所以Person原型对象上定义的属性和方法,Man都会有 // 等同于 Man.prototype.proto = Person.prototype; Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man;

function Man(name, age) { this.name = name; this.age = age; // 借用构造函数模式 Person.call(this); }

var man = new Man(‘张三丰’, 100); console.log(man); man.say(); ```

(五) call、apply 和bind有什么区别?

三者都会改变this的指向, 区别:

  1. call 和 apply 的功能相同,区别在于传参的方式不一样:,apply 的实现和 call 很类似,但是需要注意他们的参数是不一样的,apply 的第二个参数是数组或类数组。
  2. bind 和 call/apply 有一个很重要的区别,一个函数被 call/apply 的时候,会直接调用,但是 bind 会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数