JS 面向对象编程、原型链、原型继承(个人学习总结)

时间:2022-02-17 18:58:18

一、面向对象

1. 面向对象 是所有语言 都有的一种编程思想,组织代码的一种形式

  • 基于对象的语言:JS语言
  • 面向对象的语言:c++ java c#

2. 面向对象 3大特征

封装:将重用代码封装到函数 / 对象中,实现代码复用
继承:继承方法、属性(JS通过原型实现继承、其他语言通过类实现继承)
多态:同一操作针对不同对象,表现出不同的行为(JS中没有多态)

3. 面向对象的优势

  • 模块化,便于维护:将功能相近的代码封装成一个的对象,有代码更改,只需找到对应的对象进行修改即可

  • 减少全局污染,只暴露出一个构造函数,需要的话 new 一个对象就可以

4. 分析 面向过程 、面向对象

  • 面向过程:

    • 所有的细节、步骤、过程,要一步一步亲历亲为(执行者)
    • 代码量少功能简单 的场景 适用
    • 代码执行效率高,创建的变量全局变量太多,环境污染严重(ES5)
  • 面向对象:

    • 把一些功能逻辑,赋予给一个个独立的对象,对象中封装了一些列方法
    • 找到能完成这个事情的对象,让它帮你完成就行(调度者)
    • 对于大型项目,有利于团队合作开发、有利于项目代码的维护扩展,项目更新迭代;
    • 代码执行效率较低;使用面向对象思想编程可能导致代码复杂度提高
    • JQ操作DOM就是一个面向对象的方式
  • 两者关联:面向对象是对面向过程的封装

二、JS 中实现继承的3种方式

1. 混入式继承for-in:遍历为新对象添加属性、方法

<script>
    var obj = {
        name: 'zhang',
        age: 11
    };

    var o2 = {};

    for (var key in obj) {
        o2[key] = obj[key];
    }

    console.log(o2);
</script>
  • js 原生语法:没有 类似JQ中的 extend() 方法,封装如下:
<script>
    var obj = {
        name: 'zhang',
        age: 11
    };

    var extend = function (obj) {
        var o2 = {};
        for (var key in obj) {
            o2[key] = obj[key];
        }
        return o2;
    }

    console.log(extend(obj));
</script>

2. Object.create() 继承 :ES5中; IE8及以下不兼容

  • Object.create(参数的对象) 返回新对象,继承了参数对象上的所有属性、方法
<script>
    var o1 = {name: 'zhangxin'};
    var create = Object.create(o1);

    console.log(create.name);  // 'zhangxin'
</script>

3. 原型继承:实例对象 继承自 原型对象的方法/属性 【重要】

三、构造函数

1. 概念:用于创建对象(对象初始化)的一种普通的函数,一般在 new 运算符后面调用

  • 构造函数名的第一个字母,约定大写;用于区分普通函数

  • new 的作用:

    • 申请内存,创建空对象
    • 改变this指向
    • 给新对象赋值
    • 返回新对象
  • 构造函数中this的作用:为对象添加成员(属性、方法)

  • 构造函数 与 普通函数的区别:

    • 函数内部this指向不同
    • 返回返回值不同

2. 函数一创建,就有了 prototype 属性,指向原型对象

  • Math 除外,Math 是对象,不是构造函数,没有 prototype 属性

3. 构造函数中的返回值

  • 构造函数中:一般不写返回值,默认返回新创建的对象

  • 若写了返回值:

    • 返回值为引用类型:忽略新创建的对象,直接返回引用类型的返回值
    • 返回值为基本类型:忽略基本类型,直接返回新创建的对象

4. 常见的构造函数,但 Math 不是构造函数

  • ObjectFunctionArrayStringNumberRegExpData 这些构造函数都有 prototype属性

  • Math 是对象,不是函数,没有 prototype 属性

5. 构造函数的作用:对new出来的函数,进行初始化(为属性、方法赋值)

四、实例(对象)

1. 对象一创建,就有了 __proto__ 属性,指向原型对象

  • 这个属性是浏览器私有属性,一般不常用

2. 对象( null除外) 在被创建的那一刻,原型就定下来了, 即: 对象.__proto__

  • 即使修改了 构造函数.prototype 的指向,也不会影响到 已经创建好的对象 的原型对象、原型链

  • 原型对象不会被默认改变,除非手动改变指针方向,即 对象.__proto__的指向

3. 任何对象 都直接 或 间接的继承自 Object.prototype

4. 实例化过程:即创建实例对象的过程,new 构造函数

五、原型(对象)

1. 理解原型:

  • 原型:用来存放一些公共的属性或方法,让多个实例对象 访问同一个对象中的内容

  • 作用:原型继承,节省内存,实现数据共享

2. 原型对象 默认有 constructor 属性,指当前的构造函数

  • 改变构造函数.prototype原型指针方向时,最好在新的原型对象上,添加 constructor 属性

3. 读取 原型对象 指针

  • 构造函数.prototype

  • 实例对象.__proto__ 【生产环境中不使用】

  • 两个属性的区别仅仅是:站在不同的角度访问同一个对象

4. 原型对象 添加属性、方法

  • 构造函数 . prototype . 属性/方法 =‘’;

  • 实例对象 . proto . 属性/方法 =‘’; 【一般不使用】

5. *原型 Object.prototype , 万物皆对象

  • Objet.prototype = null

6. *原型上的属性 —> 任何一个对象都能直接使用(对象本身有同名属性除外)

(1) Object.prototype.constructor 指向构造函数 Object
(2) Object.prototype.toString() 判断 当前变量 属于哪种 引用类型
  • Array / String / Number / Date 都有自己的 toString() 方法,即:转为字符串 ; 验证是属于哪个引用类型,可借用 *对象上的toString()方法
<script> console.log(Object.prototype.toString()); // [object Object] console.log(Math.toString()); // [object Math] console.log(Object.prototype.toString.call([])); // [object Array] console.log(Object.prototype.toString.call('aaa')); // [object String] console.log(Object.prototype.toString.call(111)); // [object Number] console.log(Object.prototype.toString.call(new Date())); // [object Date] console.log(Object.prototype.toString.call(Array)); // [object Function] console.log(Object.prototype.toString.call(String)); // [object Function] console.log(Object.prototype.toString.call(Number)); // [object Function] console.log(Object.prototype.toString.call(Date)); // [object Function] </script>
  • 对象在隐式转换时,调用 toString()方法
<script> var obj = {}; console.log(obj + '222'); // [object Object]222 </script>
(3) Object.prototype.valueOf()
(4) Object.prototype.toLocaleString()
  • 转化为本地n的字符串
<script>
    var date = new Date();

    console.log(date);  // Sun Nov 12 2017 16:57:57 GMT+0800 (CST)
    console.log(date.toLocaleString()); // 2017/11/12 下午4:57:57
</script>
(5) 对象.hasOwnProperty("属性名") 判断该属性是不是对象本身提供的,返回布尔值
<script>
    var Fn = function () {
        this.name = 'zhangxin'
    }

    Fn.prototype = {
        age: 18
    };

    var obj = new Fn();

    var result1 = obj.hasOwnProperty('name');
    var result2 = obj.hasOwnProperty('age');
    console.log(result1);  // true
    console.log(result2); // false
</script>
(6) 原型对象.isPrototypeOf(对象) 判断括号中对象是否在原型对象的原型链上,返回布尔值
<script>
    var arr = [];

    var result = Array.prototype.isPrototypeOf(arr);
    console.log(result);  // true
</script>
(7) 对象.properyIsEnumerable(属性名) 属性是否是对象自身的 且 可枚举(遍历)

六、原型链

1. 概念

  • 任何一个对象都有原型对象, 原型对象也是对象, 所以, 原型对象也有原型对象。 这样一直往上, 就形成了一条原型对象的链式结构, 我们把这个链式结构称为: 原型链。

  • 各个对象之间:通过 proto 连接起来,形成原型链

2. 常见的原型链

obj对象的原型链:obj  ---> Object.prototype  --->  null  【最短的原型链】

arr数组的原型链:arr ---> Array.prototype  --->  Object.prototype  --->  null

fn函数的原型链:fn ---> Function.prototype ---> Object.prototype ---> null

str字符串的原型链:str ---> String.prototype ---> Object.prototype ---> null

3. Objet.prototype = null;所以 Objet.prototype是原型链结构的最上层,即万物皆对象

定义一个对象,它的原型链 就确定下来了,不会再变

定义一个函数,它的作用域链 就确定下来了,不会再变

4. 原型链分析

  • 对象( null除外) 在被创建的那一刻,原型就定下来了, 即: 对象.__proto__ ; 即使改变构造函数.prototype的指向,也不会改变已创建好对象的原型链

  • 已创建好的对象,若想改变它的原型链,设置构造函数.prototype的指向 不管用;需要设置对象.__proto__ 指向

  • in 运算符 判断的是:属性是否在 对象的原型链上

5. 完整原型链

JS 面向对象编程、原型链、原型继承(个人学习总结)

5. 属性搜索原则 沿着 原型链 进行搜索

  • 读取: 属性延原型链,一级一级向上找,一直查找到 Object.prototype 对象;属性找不到undefined;方法找不到,找不到的话报错

  • 实例对象 可以直接访问 原型对象的所有属性和方法,因为对象继承 自 原型对象。

6. 类比 变量搜索原则

  • 读取: 变量延作用域链查找声明,一级一级向外找,首先在当前作用域找声明,若没有就到n-1级链查找,直到找到0级链(全局变量);找不到 就报错,因为未声明的变量

七、JS面向对象编程 —> 【原型继承】

1. 基于原型链重新理解 原型继承:(实例对象 继承自 原型对象的方法/属性)

  • 任何对象都有一条原型链存在,所谓的原型继承就是通过任何手段,改变原型链的层次结构(即:构造函数.prototype 的指向),对象通过访问原型链中的属性或者方法,从而实现继承。

  • 应用了:实例对象可以直接访问原型对象中的属性、方法

2. 面向对象编程, 最佳实践:

  • 将对象的大部分属性:放到构造函数中

  • 将对象的所有方法、公共属性:放到原形对象中

  • 构造函数中属性复用,原型对象中方法继承

  • 通过 new 构造函数 创建的对象,实例对象 继承自原型对象上的属性 / 方法

3. 理解面向对象编程

  • 面向对象编程是根据功能,将代码拆解为一个个独立的功能体;

  • 独立的功能体 即 模块,模块化编程,降低了耦合度;较少全局污染;

  • 一般情况下一个单独个体封装在一个JS文件中,JS文件中值暴露出一个构造函数

  • 引入JS文件,模块外部,需要这个功能体时,new 构造函数 即可

(function(window) {

    var Bird = function(options) {
        this.name = options.name;
        this.age = options.age;
    };

    Bird.prototype = {
        constructor: Bird,
        action() {
            console.log(this.name);
            console.log(age);
        }
    };

    window.Bird = Bird;
})(window);

八、instanceof — 被检测对象是否在 构造函数的原型链上

  • 语法:被检测对象 instanceof 构造函数 返回布尔值

  • 可类比:原型对象.isPrototypeOf(对象) 判断括号中对象是否在原型对象的原型链上,返回布尔值

  • 作用:

    • 检测 对象是否在构造函数的原型链上
    • 检测 数组
  • 规律:

    • 创建对象后:修改 构造函数.prototype的值, 检测表达式 返回 false
    • 创建对象后:不修改 构造函数.prototype的值, 检测表达式 返回 true
  • 任何对象 instanceof Object 值都是 true

<script>
    var Person = function () {}

    var a = new Person();
    Person.prototype = {};

    console.log(a instanceof Person);  // false
    console.log(Person.prototype.isPrototypeOf(a));   // false
    // 此时:a的原型链: a --- {} --- Object.prototype --- null
</script>

九、in — 判断 属性是否在对象的原型链上

<script>
    var Fn = function () {
        this.name = 'zhang';
    }

    Fn.prototype.age = 19;

    var obj = new Fn();

    console.log('name' in obj);     // true
    console.log('age' in obj);      // true
</script>
  • 任何一个未声明的变量,都在 window的原型链上
console.log('a' in window); // true

十、delete — 删除 对象上的属性

<script> var obj = { name: 'zhangxi', age: 18 }; delete obj.age; console.log(obj); // {name: 'zhangxi'} </script>

十、静态成员 、 实例成员

成员:属性 和 方法

私有/静态成员

  • 由构造函数直接能够访问到的,跟构造函数相关的属性和方法。

共有/实例成员

  • 由实例对象直接访问到的,与实例对象向关联的属性和方法。

最佳实践

  • 如果静态成员、实例成员中有一个功能相似的方法,将逻辑代码放到静态成员中,实例成员只需调用静态成员的方法即可使用!
  • $ 或 jQuery,在jQuery库中可以被看作是 构造函数,其后跟的方法,静态成员!

  • $() 这个方法的返回值就是:jQuery对象(jQuery的实例对象),其后跟的方法,实例成员!

each 方法由两种使用方式:
$('div').each(function(index,value) {});   实例成员
$.each(arr,function(index,value) {});     静态成员 $.ajax()

十一、易混知识点

1. Function 是JS中的一等公民,自生;在原型链中即作为对象,又作为函数

2. Function: Function创造 Object 和 function

3. 构造函数的类型:function;【特殊】:Math 是对象,不是构造函数,其类型object

4. 构造函数的原型的类型:object;【特殊】:Function.prototype 的类型为function

5. 引用类型的 类型:object;【特殊】:function fn() {} 的类型为function

5. null 不是对象,但位于原型链的最顶层; 通常用 null 来代表一个空对象