面向对象html
抽象性
所谓的抽象性就是:若是须要一个对象描述数据,须要抽取这个对象的核心数据程序员
封装性
对象是将数据与功能组合到一块儿,即封装编程
继承性
所谓继承性就是本身没有可是别人有,拿过来成为本身的,就是继承,继承是实现复用的一种手段数组
在JS中没有明确的继承语法(ES6提供了class extend语法),通常都是按照继承的理念实现对象的成员扩充实现继承,所以JS中实现继承的方法很是对多。缓存
传统继承基于类,JS继承基于对象闭包
一个简单的继承模式:混入(mix)app
function mix ( o1, o2 ) { for ( var k in o2 ) { o1[ k ] = o2[ k ]; } }
类class:在JS中就是构造函数编程语言
实例(instance)与对象(object)函数
键值对与属性和方法性能
父类与子类(基类和派生类)
构造函数
var p = new Person();
new
建立了一个对象,相似于{}
,是一个没有任何(自定义)成员的对象。
new
建立对象,那么对象的类型就是建立他的构造函数名{}
不管如何都是Object类型,至关于new Object
做用域
域表示的就是范围,即做用域,就是一个名字在什么地方可使用,何时不能使用。
闭包
什么是闭包?
闭包的含义就是闭合,包起来,简单的来讲,就是一个具备封闭功能与包裹功能的结构。所谓的闭包就是一个具备封闭的对外不公开的,包裹结构,或空间。
为何函数能够构成闭包?
闭包就是一个具备封闭与包裹功能的结构,是为了实现具备私有访问空间的函数的。函数能够构成闭包。函数内部定义的数据函数外部没法访问,即函数具备封闭性;函数能够封装代码即具备包裹性,因此函数能够构成闭包。
闭包不容许外部访问,要解决的问题就是让外部间接访问函数内部的数据。事实上,经过使用闭包,咱们能够作不少事情。好比模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提高代码的执行效率。利用闭包能够实现以下需求:
函数执行须要内存,那么函数中定义的变量,会在函数执行结束后自动回收,凡是由于闭包结构的,被引出的数据,若是还有变量引用这些数据的话,那么这些数据就不会被回收。所以在使用闭包的时候若是不使用某些数据了,必定要赋值一个null
var f = (function () { var num = 123; return function () { return num; }; })(); // f 引用着函数,函数引用着变量num // 所以在不使用该数据的时候,最好写上 f = null;
原型
一句话说明什么是原型:原型能存储咱们的方法,构造函数建立出来的实例对象可以引用原型中的方法。
JS中一切皆对象,而每一个对象都有一个原型(Object除外),这个原型,大概就像Java中的父类,因此,基本上你能够认为原型就是这个对象的父对象,即每个对象(Object除外)内部都保存了它本身的父对象,这个父对象就是原型。通常建立的对象若是没有特别指定原型,那么它的原型就是Object(这就很相似Java中全部的类默认继承自Object类)。
ES6经过引入class ,extends等关键字,以一种语法糖的形式把构造函数包装成类的概念,更便于你们理解。是但愿开发者再也不花精力去关注原型以及原型链,也充分说明原型的设计意图和类是同样的。
当对象被建立以后,查看它们的原型的方法不止一种,之前通常使用对象的__proto__
属性,ES6推出后,推荐用Object.getPrototypeOf()
方法来获取对象的原型
function A(){ this.name='lala'; } var a=new A(); console.log(a.__proto__) //输出:Object {} //推荐使用这种方式获取对象的原型 console.log(Object.getPrototypeOf(a)) //输出:Object {}
不管对象是如何建立的,默认原型都是Object,在这里须要说起的比较特殊的一点就是,经过构造函数来建立对象,函数A自己也是一个对象,而A有两个指向表示原型的属性,分别是proto和prototype,并且两个属性并不相同
function A(){ this.name='lala'; } var a=new A(); console.log(A.prototype) //输出:Object {} console.log(A.__proto__) //输出:function () {} console.log(Object.getPrototypeOf(A)) //输出:function () {}
函数的的prototype属性只有在看成构造函数建立的时候,把自身的prototype属性值赋给对象的原型。而实际上,做为函数自己,它的原型应该是function对象,而后function对象的原型才是Object。
总之,建议使用ES6推荐的查看原型和设置原型的方法。
其实原型和类的继承的用法是一致的:当你想用某个对象的属性时,将当前对象的原型指向该对象,你就拥有了该对象的使用权了。
function A(){ this.name='world '; } function B(){ this.bb="hello" } var a=new A(); var b=new B(); //将b设置为a的原型,此处有一个问题,即a的constructor也指向了B构造函数,可能须要纠正 Object.setPrototypeOf(a,b); a.constructor=A; console.log(a.bb); //hello
若是使用ES6来作的话则简单许多,甚至不涉及到prototype这个属性
class B{ constructor(){ this.bb='hello' } } class A extends B{ constructor(){ super(); this.name='world'; } } var a=new A(); console.log(a.bb+" "+a.name); //hello world console.log(typeof(A)) //"function"
怎么样?是否是已经彻底看不到原型的影子了?活脱脱就是类继承,可是你也看获得实际上类A 的类型是function,因此说,本质上class在JS中是一种语法糖,JS继承的本质依然是原型,不过,ES6引入class,extends 来掩盖原型的概念也是一个很友好的举动,对于长期学习那些类继承为基础的面对对象编程语言的程序员而言。
个人建议是,尽量理解原型,尽量用class这种语法糖。
好了,问本身两个问题:
构造函数.prototype.xxxx = vvv
Student.prototype = { sayHello : function(){}, study : function(){} };
什么是原型链?
凡是对象就有原型,那么原型又是对象,所以凡是给定一个对象,那么就能够找到他的原型,原型还有原型,那么如此下去,就构成一个对象的序列,称该结构为原型链。
这个概念其实也变得比较简单,能够类比类的继承链条,即每一个对象的原型往上追溯,一直到Object为止,这组成了一个链条,将其中的对象串联起来,当查找当前对象的属性时,若是没找到,就会沿着这个链条去查找,一直到Object,若是还没发现,就会报undefined。
原型链的结构
凡是使用构造函数,建立出对象,而且没有利用赋值的方式修改原型,就说该对象保留默认的原型链。
默认原型链结构是什么样子呢?
function Person(){} var p = new Person(); //p 具备默认的原型链
默认的原型链结构就是:当前对象 -> 构造函数.prototype -> Object.prototype -> null
在实现继承的时候,有时候会利用替换原型链结构的方式实现原型继承,那么原型链结构就会发生改变
function DunizbCollection(){} DunizbCollection.prototype = []; var arr = new DunizbCollection();
此时arr对象的原型链结构被指向了数组对象的原型链结构了:arr -> [] -> Array.prototype -> Object.prototype -> null
用图形表示对象的原型链结构
以以下代码为例绘制原型链结构
function Person(){} var p = new Person();
原型链结构图为:
使用原型须要注意两点:
继承
实现继承有两种常见方式:
var Person = function () {}; Person.prototype.extend = function ( o ) { for ( var k in o ) { this[ k ] = o[ k ]; } }; Person.prototype.extend({ run: function () { console.log( '我能跑了' ); }, eat: function () { console.log( '我能够吃了' ); }, sayHello: function () { console.log( '我吃饱了' ); } });
apply()
和call()
方法也能够在(未来)新建立的对象上执行构造函数 function Person ( name, age, gender ) { this.name = name; this.age = age; this.gender = gender; } // 须要提供一个 Student 的构造函数建立学生对象 // 学生也应该有 name, age, gender, 同时还须要有 course 课程 function Student ( name, age, gender, course ) { Person.call( this, name, age, gender ); this.course = course; }
在《JavaScript高级程序设计(第三版)》中详细介绍了继承的6种方式
函数的四种调用模式
就是一个简单的函数调用。函数名的前面没有任何引导内容。
方法必定式依附与一个对象,将函数赋值给对象的一个属性,那么就成为了方法。
建立对象的时候构造函数作了什么?因为构造函数只是给 this 添加成员,没有作其余事情。而方法也能够完成这个操做,就是 this 而言,构造函数与方法没有本质的区别。
特征
建立对象的模式
上下文就是环境。就是本身定义设置 this 的含义。
语法
描述
参数问题
不管是 call 仍是 apply 在没有后面的参数的状况下(函数无参数,方法五参数)是彻底一致的
function foo(){ console.log( this ); } foo.apply( obj ); foo.call( obj );
第一个参数的使用也是有规则的:
若是不传入参数,或传入 null 、undefined 等,那么至关于 this 默认为 window
foo(); foo.apply(); foo.apply( null ); foo.call( undefined );
在使用上下文调用的时候,原函数(方法)可能会带有参数,那么这个参数再上下文调用中使用 第二个(第 n 个)参数来表示
function foo( num ) { console.log( num ); } foo.apply( null, [ 123 ] ); // 等价于 foo( 123 );
参考资料
做者: Dunizb 连接:http://www.imooc.com/article/13649来源:慕课网本文原创发布于慕课网 ,转载请注明出处,谢谢合做!