时间:2021-05-26
本文实例讲述了JavaScript面向对象核心知识与概念。分享给大家供大家参考,具体如下:
抽象性
所谓的抽象性就是:如果需要一个对象描述数据,需要抽取这个对象的核心数据
封装性
对象是将数据与功能组合到一起,即封装
继承性
所谓继承性就是自己没有但是别人有,拿过来成为自己的,就是继承,继承是实现复用的一种手段
在JS中没有明确的继承语法(ES6提供了class extend语法),一般都是按照继承的理念实现对象的成员扩充实现继承,因此JS中实现继承的方法非常对多。
传统继承基于类,JS继承基于对象
一个简单的继承模式:混入(mix)
function mix ( o1, o2 ) { for ( var k in o2 ) { o1[ k ] = o2[ k ]; }}类class:在JS中就是构造函数
实例(instance)与对象(object)
键值对与属性和方法
父类与子类(基类和派生类)
首先运算符new创建了一个对象,类似于{},是一个没有任何(自定义)成员的对象。
然后调用构造函数,为其初始化成员
域表示的就是范围,即作用域,就是一个名字在什么地方可以使用,什么时候不能使用。
简单的说,作用域是针对变量的,比如我们创建一个函数 a1,函数里面又包了一个子函数 a2。
此时就存 在三个作用域:全局作用域,a1 作用域,a2 作用域;即全局作用域包含了 a1 的作用域,a2 的作用域包含了 a1 的作用域。
当 a2 在查找变量的时候会先从自身的作用域区查找,找不到再到上一级 a1 的作用域查找,如果还没找到就
到全局作用域区查找,这样就形成了一个作用域链。
实用闭包主要是为了设计私有方法和变量。闭包的优点是可以避免全局变量的污染;缺点是闭包会常驻内存,增加内存使用量,使用不当很容易造成内存泄露。在JavaScript中,函数即闭包,只有函数才能产生作用域。
闭包有3个特性:
闭包的作用,就是保存自己私有的变量,通过提供的接口(方法)给外部使用,但外部不能直接访问该变量。
通过使用闭包,我们可以做很多事情,比如模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提升代码的执行效率。利用闭包可以实现如下需求:
一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。
闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
对象模式
函数内部定义个一个对象,对象中绑定多个函数(方法),返回对象,利用对象的方法访问函数内的数据
函数模式
函数内部定义一个新函数,返回新函数,用新函数获得函数内的数据
沙箱模式
沙箱模式就是一个自调用函数,代码写到函数中一样会执行,但是不会与外界有任何的影响,比如jQuery
js 垃圾回收机制,也就是当一个函数被执行完后,其作用域会被收回,如果形成了闭包,执行完后其作用域就不会被收回。
函数执行需要内存,那么函数中定义的变量,会在函数执行结束后自动回收,凡是因为闭包结构的,被引出的数据,如果还有变量引用这些数据的话,那么这些数据就不会被回收。因此在使用闭包的时候如果不使用某些数据了,一定要赋值一个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 worldconsole.log(typeof(A)) //"function"怎么样?是不是已经完全看不到原型的影子了?活脱脱就是类继承,但是你也看得到实际上类A 的类型是function,所以说,本质上class在JS中是一种语法糖,JS继承的本质依然是原型,不过,ES6引入class,extends 来掩盖原型的概念也是一个很友好的举动,对于长期学习那些类继承为基础的面对对象编程语言的程序员而言。
我的建议是,尽可能理解原型,尽可能用class这种语法糖。
好了,问自己两个问题:
为什么属性不放在原型上而方法要放在原型上?
什么是原型链?
凡是对象就有原型,那么原型又是对象,因此凡是给定一个对象,那么就可以找到他的原型,原型还有原型,那么如此下去,就构成一个对象的序列,称该结构为原型链。
这个概念其实也变得比较简单,可以类比类的继承链条,即每个对象的原型往上追溯,一直到Object为止,这组成了一个链条,将其中的对象串联起来,当查找当前对象的属性时,如果没找到,就会沿着这个链条去查找,一直到Object,如果还没发现,就会报undefined。
原型链的结构
凡是使用构造函数,创建出对象,并且没有利用赋值的方式修改原型,就说该对象保留默认的原型链。
默认原型链结构是什么样子呢?
默认的原型链结构就是:当前对象 -> 构造函数.prototype -> Object.prototype -> null
在实现继承的时候,有时候会利用替换原型链结构的方式实现原型继承,那么原型链结构就会发生改变
function DunizbCollection(){}DunizbCollection.prototype = [];var arr = new DunizbCollection();此时arr对象的原型链结构被指向了数组对象的原型链结构了:arr -> [] -> Array.prototype -> Object.prototype -> null
用图形表示对象的原型链结构
以如下代码为例绘制原型链结构
原型链结构图为:
使用原型需要注意两点:
实现继承有两种常见方式:
最简单的继承就是将别的对象的属性强加到我身上,那么我就有这个成员了。
混合式继承的简单描述:
利用原型也可以实现继承,不需要在我身上添加任何成员,只要原型有了我就有了。
这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数,而函数只不过是在特定环境中执行代码的对象,因此通过使用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种方式
就是一个简单的函数调用。函数名的前面没有任何引导内容。
function foo () {}var func = function () {};...foo();func();(function () {} )();this 的含义:在函数中 this 表示全局对象,在浏览器中式 window
方法一定式依附与一个对象,将函数赋值给对象的一个属性,那么就成为了方法。
function f() { this.method = function () {};}var o = { method: function () {}}this 的含义:这个依附的对象
创建对象的时候构造函数做了什么?由于构造函数只是给 this 添加成员,没有做其他事情。而方法也可以完成这个操作,就是 this 而言,构造函数与方法没有本质的区别。
特征:
构造函数中不需要 return ,就会默认的 return this。
上下文就是环境。就是自己定义设置 this 的含义。
语法
描述
参数问题
无论是 call 还是 apply 在没有后面的参数的情况下(函数无参数,方法五参数)是完全一致的
第一个参数的使用也是有规则的:
在使用上下文调用的时候,原函数(方法)可能会带有参数,那么这个参数再上下文调用中使用 第二个(第 n 个)参数来表示
function foo( num ) { console.log( num );}foo.apply( null, [ 123 ] );// 等价于foo( 123 );参考资料
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
导语前面的系列文章,基本把JavaScript的核心知识点的基本语法、标准库等章节讲解完;本章开始进入JavaScript核心知识点的高级部分——面向对象的程序
上几节讲了JavaScript面向对象之命名空间、javascript面向对象的JavaScript类与JavaScript面向对象的之私有成员和公开成员,大家
面向对象的语言(如Java)中有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,JavaScript没有类的概念,因此它的对象也与基于类的语言
00方法遵循一般的认知方法学的基本概念(即有关演绎一从一般到特殊和归纳一从特殊到一般的完整理论和方法体系)而建立面向对象方法学。面向对象方法学要点之一:认为客观
类在很多面向对象的语言中有类(class)的概念,对象是类的实例。Lua中不存在类的概念。Lua就像JavaScript一样是面向原型的语言(http://en