扩展内置对象
- 内置对象: Object | Array | Date | Function | String ...
- 扩展: 给内置的对象添加属性和方法
- 注意: 此方法可以达到扩展内置对象的效果.
- 不建议使用: 在实际开发中, 可能多人合作开发. 若多人以此方法扩展对象, 代码难以维护, 可能代码被覆盖导致安全性差
安全的扩展内置对象
- 核心操作
- 自定义一个构造函数
-
设置这个构造函数的原型对象是内置构造函数的一个实例
原型链
- 原型链的顶端:
Object.prototype
- 原型链最顶端的原型:
Object.prototype.__proto__ = null;
- 原型链最顶端的原型:
- 原型链结构
- 每个对象都是由构造函数创建出来的
- 每一个构造函数都有对应的原型对象
- 构造函数的原型对象也是一个对象
- 因此, 构造函数的原型对象也有构造函数
- 构造函数的原型对象的构造函数也有原型对象, 这个原型对象也是一个对象
原型链中属性的搜索原则
- 就近原则
- 访问对象属性时, 先查找自身是否有该属性, 有直接使用
- 若没有, 就访问这个对象的原型对象是否有, 有就直接使用
- 若没有, 会访问这个对象原型对象的原型对象上是否有, 如果有就直接使用
-
直到Object的原型对象, 若没有, 则返回undefined(属性)或报错(方法)
继承的实现
- 属性拷贝(混入式继承)
-
方法: 如果属性的值是引用类型的数据, 子对象和父对象共享同一份数据, 修改其中一个会影响另一个
var obj = {name : 'zs',age : 20,friends:['小明','小红']}; var obj1 = {}; // 需求:obj1获取obj的属性 for(var key in obj){ obj1[key] = obj[key]; } // 修改了obj1的friends对obj的friends会有影响,因为他们存储的是同一个数组的地址 obj1.friends.push('老王'); console.log(obj1); console.log(obj);
函数:
Object.assign(子对象, 父对象1, 父对象2,...);
-
- 原型式继承
- 方法: 父构造函数的原型对象赋值给子构造函数的原型对象(替换原型
Son.prototype = Father.prototype
) - 问题
- 创建出来的对象, 构造器属性, 默认指向父构造函数
- 无法通过构造器属性进行修改
- 不能获取实例属性/方法, 只能获取父构造函数原型对象的属性和方法
- 创建出来的对象, 构造器属性, 默认指向父构造函数
- 方法: 父构造函数的原型对象赋值给子构造函数的原型对象(替换原型
- 原型链继承
-
方法
- 提供一个父构造函数
- 提供一个子构造函数
- 设置子构造函数的原型对象是父构造函数的实例
-
注意点
- 修正构造器属性: 要在设置原型链继承之后
- 设置原型对象的属性: 要在设置原型链继承之後
-
设置了原型链继承之後, 只能利用对象的动态特性设置原型对象, 不能使用字面量的方式
-
问题
- 无法传递参数给父构造函数-13
- 继承过来的实例属性会成为原型属性, 对创建出来的对象存在数据共享的问题
-
- 复杂的原型链实例
- 注意点: 同原型链继承一样
- 借用构造函数继承
- 方法: 只能获取实例属性和方法, 不能获取原型属性和方法
- 组合继承
- 方法
- 借用构造函数继承获取实例属性和方法
-
使用原型链继承获取原型属性和方法
- 方法
Object.create()
- 定义: 创建一个新的对象, 并且设置这个对象的原型对象是传入的参数
- 兼容: ES5支持
call() | apply() |bind
- 在ES3, 给Function的原型对象添加2个方法
Function.prototype.call | Function.prototype.apply
- 作用: 借用其他对象的方法
- 参数
- 第一个参数: 调用方法的对象(函数内部this的绑定对象)
- 后面的参数:
call 参数列表 参数1, 参数2...
-
apply 参数列表[参数1, 参数2]
//打印1: 期望传递一个参数(参数列表可不传) console.log(Function.prototype.call.length); //打印2: 期望传递两个参数(调用对象的数组, []) console.log(Function.prototype.apply.length);
深拷贝/浅拷贝
- 浅拷贝: 引用类型数据只拷贝到地址
- 深拷贝: 内容拷贝
- 实现
- 提供一个函数, 函数有2个参数, 一个目标对象, 一个要拷贝属性的对象
- 判断: 第一个参数是否有值
- 遍历第二个参数, 判断属性值的类型
- 如果是值类型的数据就直接赋值
- 如果是引用类型的数据, 就再一次调用这个方法拷贝内部存储的数据
- 实现
Array.isArray()
定义: 判断一个对象是否是数组
-
ES5的方法, ES5之前不支持,故存在兼容性问题
// 兼容写法 if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; }
-
其他方法:
Object.prototype.toString.call()
let a = [1,2,3] Object.prototype.toString.call(a) === '[object Array]';//true //检验是否是函数 let a = function () {}; Object.prototype.toString.call(a) === '[object Function]';//true //检验是否是数字 let b = 1; Object.prototype.toString.call(a) === '[object Number]';//true //PS: 甚至对于多全局环境时, Object.prototype.toString().call()也能符合预期处理判断。 //为body创建并添加一个iframe标签 var iframe = document.createElement('iframe'); document.body.appendChild(iframe); //取得iframe对象的构造数组方法 xArray = window.frames[window.frames.length-1].Array; //通过构造函数获取一个实例 var arr = new xArray(1,2,3); console.log(Object.prototype.toString.call(arr) === '[object Array]');//true // 另外两种方法:存在多个全局环境的问题 arr instanceof Array;//false arr.constructor === Array;//false