面向对象
面向对象编程(Object Oriented Programming,缩写OOP):将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟,其有三个特征:封装、继承和多态
对象:
- 对象是单个实物的抽象
- 对象是一个容器,封装了属性(property)和方法(method)
构造函数:就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构,为了与普通函数区别,构造函数名字的第一个字母通常大写
有两个特点:
- 函数体内部使用了
this
关键字,代表了所要生成的对象实例 - 生成对象的时候,必须使用
new
命令
new
命令运行的基本原理:
- 创建一个空对象,作为将要返回的实例对象
- 将这个空对象的原型,指向构造函数的
prototype
属性 - 将空对象赋值给函数内部的
this
关键字 - 开始执行构造函数内部的代码
new
命令的内部运行代码:
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
// 将 arguments 对象转为数组
var args = [].slice.call(arguments);
// 取出构造函数
var constructor = args.shift();
// 创建一个空对象,继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype);
// 执行构造函数
var result = constructor.apply(context, args);
// 如果返回结果是对象,就直接返回,否则返回 context 对象
return (typeof result === 'object' && result != null) ? result : context;
}
// 实例
function Person(name, age) {
this.name = name;
this.age = age;
}
var actor = _new(Person, '张三', 28);
使用new.target
属性可以判断函数调用的时候,是否使用new
命令
Object.create() 创建实例对象:
构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()
方法,继承了前者的属性和方法
this
关键字
简单说,this
就是属性或方法“当前”所在的对象
- 它总是返回一个对象
- 由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即
this
的指向是可变的 - 它的设计目的就是在函数体内部,指代函数当前的运行环境
使用场合
- 事件调用环境——谁触发事件,指向谁
- 全局环境——window(浏览器环境)、module.exports(node环境),严格模式都是undefined
- 构造函数——构造函数中的
this
,指的是实例对象 - 对象的方法——如果对象的方法里面包含
this
,this
的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this
的指向 - 在箭头函数中——指的是函数定义生效时所在的对象,而不是使用时所在的对象
this
的五种绑定规则(除了箭头函数,this总是指向调用该函数的对象)
- 默认绑定(严格/非严格模式):指向全局对象或者
undefined
- 隐式绑定:上下文对象
- 显示绑定:使用
call
、apply
、bind
等 new
绑定- 箭头函数绑定:
- 箭头函数不绑定
this
,箭头函数中的this相当于普通变量,指向外层作用域,对象没有单独的作用域 - 箭头函数的this
寻值行为与普通变量相同,在作用域中逐级寻找 - 箭头函数的this
无法通过bind
,call
,apply
来直接修改(可以间接修改) - 改变作用域中this的指向可以改变箭头函数的this
-eg. function closure(){()=>{//code }}
,在此例中,我们通过改变封包环境closure.bind(another)()
,来改变箭头函数this
的指向
使用注意点
- 避免多层
this
,由于this
的指向是不确定的,所以切勿在函数中包含多层的this
,使用一个变量固定this
的值,然后内层函数调用这个变量,是非常常见的做法 - 避免数组处理方法中的
this
,数组的map
和foreach
方法,允许提供一个函数作为参数。这个函数内部不应该使用this
,解决这个问题的一种方法,就是前面提到的,使用中间变量固定this
;另一种方法是将this
当作foreach
方法的第二个参数,固定它的运行环境 - 避免回调函数中的
this
,回调函数中的this
往往会改变指向,最好避免使用,为了解决这个问题,可以采用下面的一些方法对this
进行绑定,也就是使得this
固定指向某个对象,减少不确定性
绑定 this
的方法
this
的动态切换,固然为 JavaScript
创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this
固定下来,避免出现意想不到的情况。JavaScript
提供了call
、apply
、bind
这三个方法,来切换/固定this
的指向
Function.prototype.call()
:函数实例的call
方法,可以指定函数内部this
的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数,call
方法的参数,应该是一个对象。如果参数为空、null
和undefined
,则默认传入全局对象,如果call
方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call
方法,形式如func.call(thisValue, arg1, arg2, ...)
Function.prototype.apply()
:apply
方法的作用与call
方法类似,也是改变this
指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下:func.apply(thisValue, [arg1, arg2, ...])
,利用这一点,可以做一些有趣的应用: - 找出数组最大元素 - 将数组的空元素变为undefined
- 转换类似数组的对象 - 绑定回调函数的对象Function.prototype.bind()
:bind()
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数,bind()
方法有一些使用注意点: - 每一次返回一个新函数 - 结合回调函数使用 - 结合call()
方法使用
原型与原型链
prototype
属性:每个函数都有一个prototype
属性,指向一个对象,原型对象的所有属性和方法,都能被实例对象共享
function f(){}
f.prototype // f {}
typeof f.prototype // 'object'
函数默认具有prototype
属性,对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型
总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象
原型链:JavaScript 规定,所有对象都有自己的原型对象(prototype
)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain
):对象到原型,再到原型的原型……
如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype
,即Object
构造函数的prototype
属性。也就是说,所有对象都继承了Object.prototype
的属性。这就是所有对象都有valueOf
和toString
方法的原因,因为这是从Object.prototype
继承的
那么,Object.prototype
对象有没有它的原型呢?回答是Object.prototype
的原型是null
。null
没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null
constructor属性:prototype
对象有一个constructor
属性,默认指向prototype
对象所在的构造函数
function P(){}
P.prototype.constructor === P // Function: P
由于constructor
属性定义在prototype
对象上面,意味着可以被所有实例对象继承
function P() {}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true
p.hasOwnProperty('constructor') // false
constructor
属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的
修改原型对象时,一般要同时修改constructor
属性的指向
如果不能确定constructor
属性是什么函数,还有一个办法:通过name
属性,从实例得到构造函数的名称
function Foo() {}
var f = new Foo();
f.constructor.name // "Foo"
__proto__属性:这个属性是每个实例对象特有的(当然构造函数也有),指向函数的prototype
原型对象,让实例找到自己的原型对象,查找原型链用的
function P(){}
var p = new P();
p.__proto__ === P.prototype // P {}
function A(){}
var a = new A();
A.prototype // A {}
Function.prototype // [Function]
Object.prototype // {}
a.__proto__ // A {}
a.__proyo__.__proto__ // {}
a.__proto__.__proto__.__proto__ // null
Object对象相关方法:
-
Object.getPrototypeOf()
:返回参数对象的原型。这是获取原型对象的标准方法var F = function () {}; var f = new F(); Object.getPrototypeOf(f) === F.prototype // true
下面是几种特殊对象的原型
// 空对象的原型是 Object.prototype Object.getPrototypeOf({}) === Object.prototype // true // Object.prototype 的原型是 null Object.getPrototypeOf(Object.prototype) === null // true // 函数的原型是 Function.prototype function f() {} Object.getPrototypeOf(f) === Function.prototype // true
-
Object.setPrototypeOf()
:为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象var a = {}; var b = {x: 1}; Object.setPrototypeOf(a, b); Object.getPrototypeOf(a) === b // true a.x // 1
-
Object.create()
:该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。该实例完全继承原型对象的属性// 原型对象 var A = { print: function () { console.log('hello'); } }; // 实例对象 var B = Object.create(A); Object.getPrototypeOf(B) === A // true B.print() // hello B.print === A.print // true
-
Object.prototype.isPrototypeOf()
:实例对象的isPrototypeOf
方法,用来判断该对象是否为参数对象的原型var o1 = {}; var o2 = Object.create(o1); var o3 = Object.create(o2); o2.isPrototypeOf(o3) // true o1.isPrototypeOf(o3) // true
-
Object.prototype.__proto__
:实例对象的__proto__
属性(前后各两个下划线),返回该对象的原型 -
获取原型对象方法的比较:
obj.__proto__
obj.constructor.prototype
Object.getPrototypeOf(obj)
推荐
-
Object.getOwnPropertyNames
:返回一个数组,成员是参数对象本身的所有属性的键名,不包含继承的属性键名 -
Object.prototype.hasOwnProperty()
:返回一个布尔值,用于判断某个属性定义在对象自身,还是定义在原型链上 -
Object.keys()
:用来遍历对象的属性,参数是一个对象,返回一个数组;该数组的成员都是该对象自身而不是继承的所有属性名 -
Object.getOwnPropertyNames()
:返回包括不可枚举的所有属性名 -
Object.prototype.valueOf()
:返回一个对象的‘值’,默认情况下返回对象本身 -
Object.prototype.toString()
:返回一个对象的字符串形式,默认情况下返回类型字符串,其主要应用是判断数据类型
参考链接
- 网道/WangDoc.com-JavaScript教程/面向对象编程-wangdoc.com/javascript/….
- Object对象的静态方法-wangdoc.com/javascript/…
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!