一、面向对象
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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
2.
Object.create()
继承 :ES5中; IE8及以下不兼容Object.create(参数的对象)
返回新对象,继承了参数对象上的所有属性、方法
<script> var o1 = {name: 'zhangxin'}; var create = Object.create(o1); console.log(create.name); // 'zhangxin' </script>
- 1
- 2
- 3
- 4
- 5
- 6
3. 原型继承:实例对象 继承自 原型对象的方法/属性 【重要】
三、构造函数
1. 概念:用于创建对象(对象初始化)的一种普通的函数,一般在
new
运算符后面调用构造函数名的第一个字母,约定大写;用于区分普通函数
new 的作用:
- 申请内存,创建空对象
- 改变this指向
- 给新对象赋值
- 返回新对象
构造函数中
this
的作用:为对象添加成员(属性、方法)构造函数 与 普通函数的区别:
- 函数内部this指向不同
- 返回返回值不同
2. 函数一创建,就有了
prototype
属性,指向原型对象- Math 除外,Math 是对象,不是构造函数,没有
prototype
属性
3. 构造函数中的返回值
构造函数中:一般不写返回值,默认返回新创建的对象
若写了返回值:
- 返回值为引用类型:忽略新创建的对象,直接返回引用类型的返回值
- 返回值为基本类型:忽略基本类型,直接返回新创建的对象
4. 常见的构造函数,但
Math
不是构造函数Object
、Function
、Array
、String
、Number
、RegExp
、Data
这些构造函数都有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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 对象在隐式转换时,调用
toString()
方法
<script> var obj = {}; console.log(obj + '222'); // [object Object]222 </script>
- 1
- 2
- 3
- 4
(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>
- 1
- 2
- 3
- 4
- 5
- 6
(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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
(6)
原型对象.isPrototypeOf(对象)
判断括号中对象是否在原型对象的原型链上,返回布尔值<script> var arr = []; var result = Array.prototype.isPrototypeOf(arr); console.log(result); // true </script>
- 1
- 2
- 3
- 4
- 5
- 6
(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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3. Objet.prototype = null;所以 Objet.prototype是原型链结构的最上层,即万物皆对象
定义一个对象,它的原型链 就确定下来了,不会再变
定义一个函数,它的作用域链 就确定下来了,不会再变
4. 原型链分析
对象( null除外) 在被创建的那一刻,原型就定下来了, 即:
对象.__proto__
; 即使改变构造函数.prototype
的指向,也不会改变已创建好对象的原型链已创建好的对象,若想改变它的原型链,设置
构造函数.prototype
的指向 不管用;需要设置对象.__proto__
指向in
运算符 判断的是:属性是否在 对象的原型链上
5. 完整原型链
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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
八、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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
九、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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 任何一个未声明的变量,都在 window的原型链上
console.log('a' in window); // true
- 1
十、delete — 删除 对象上的属性
<script> var obj = { name: 'zhangxi', age: 18 }; delete obj.age; console.log(obj); // {name: 'zhangxi'} </script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
十、静态成员 、 实例成员
成员:属性 和 方法
私有/静态成员
- 由构造函数直接能够访问到的,跟构造函数相关的属性和方法。
共有/实例成员
- 由实例对象直接访问到的,与实例对象向关联的属性和方法。
最佳实践
- 如果静态成员、实例成员中有一个功能相似的方法,将逻辑代码放到静态成员中,实例成员只需调用静态成员的方法即可使用!
$ 或 jQuery,在jQuery库中可以被看作是 构造函数,其后跟的方法,静态成员!
$() 这个方法的返回值就是:jQuery对象(jQuery的实例对象),其后跟的方法,实例成员!
each 方法由两种使用方式: $('div').each(function(index,value) {}); 实例成员 $.each(arr,function(index,value) {}); 静态成员 $.ajax()
- 1
- 2
- 3
十一、易混知识点
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 来代表一个空对象