JavaScript面向对象核心知识归纳

面向对象

概念

  1. 面向对象就是使用对象。面向对象开发就是使用对象开发。
  2. 面向过程就是用过程的方式进行开发。面向对象是对面向过程的封装。

三大特性

抽象性
所谓的抽象性就是:如果需要一个对象描述数据,需要抽取这个对象的核心数据

  1. 提出需要的核心属性和方法
  2. 不在特定的环境下无法明确对象的具体意义

封装性
对象是将数据与功能组合到一起,即封装

  1. JS对象就是键值对的集合,键值如果是数据(基本数据、符合数据、空数据)就称为属性,如果键值是函数那么就称为方法
  2. 对象就是将属性与方法封装起来
  3. 方法是将过程封装起来

继承性
所谓继承性就是自己没有但是别人有,拿过来成为自己的,就是继承,继承是实现复用的一种手段

  • 在Java等语言中继承满足一个class的规则,类是一个class,他规定了一个对象有什么属性和方法。
  • 在这些语言中继承是class之间的继承,一个class继承另一个class,那么该class就有了另一个class的成员,那么由该class创建出来的对象就同时具有两个class的成员。

在JS中没有明确的继承语法(ES6提供了class extend语法),一般都是按照继承的理念实现对象的成员扩充实现继承,因此JS中实现继承的方法非常对多。

传统继承基于类,JS继承基于对象

一个简单的继承模式:混入(mix)

function mix ( o1, o2 ) {
    for ( var k in o2 ) {
        o1[ k ] = o2[ k ];
    }
}

关于面向对象的一些其他概念

类class:在JS中就是构造函数

  • 在传统的面向对象语言中,使用一个叫类的东西定义模板,然后使用模板创建对象。
  • 在构造方法中也具有类似的功能,因此也称其为类

实例(instance)与对象(object)

  • 实例一般是指某一个构造函数创建出来的对象,我们称为XXXX 构造函数的实例
  • 实例就是对象。对象是一个泛称
  • 实例与对象是一个近义词

键值对与属性和方法

  • 在JS中键值对的集合称为对象
  • 如果值为数据(非函数),就称该键值对为属性
  • 如果值为函数(方法),就称该键值对为方法method

父类与子类(基类和派生类)

  • 传统的面向对象语言中使用类来实现继承那么就有父类、子类的概念
  • 父类又称为基类,子类又称为派生类
  • 在JS中没有类的概念,在JS中常常称为父对象,子对象,基对象,派生对象。

构造函数

构造函数是干什么用的

  1. 初始化数据的
  2. 在JS中给对象添加属性用的,初始化属性值用的

创建对象的过程

  1. 代码:var p = new Person();
  2. 首先运算符new创建了一个对象,类似于{},是一个没有任何(自定义)成员的对象。
    • 使用new 创建对象,那么对象的类型就是创建他的构造函数名
    • 使用{}无论如何都是Object类型,相当于new Object
  3. 然后调用构造函数,为其初始化成员
    • 构造函数在调用的一开始,有一个赋值操作,即this = 刚刚创建出来的对象。
    • 因此在构造函数中this表示刚刚创建出来的对象。
  4. 在构造函数中 利用 对象的动态特性 为其对象添加成员。

作用域

什么是作用域

域表示的就是范围,即作用域,就是一个名字在什么地方可以使用,什么时候不能使用。

JS中词法作用域的规则

  1. 函数允许访问函数外部的数据
  2. 整个代码结构中只有函数可以限定作用域
  3. 作用规则首先使用提升规则分析
  4. 如果当前作用域中有了名字了,就不考虑外面的名字

属性搜索原则

  • 所谓的属性搜索原则,就是对象在访问属性或方法的时候,首先在当前对象中查找
  • 如果当前对象中存储着属性或方法,停止查找,直接使用该属性或方法
  • 如果当前对象没有该成员,那么再在其原型对象中查找
  • 如果原型对象中含有该成员,那么停止查找,直接使用
  • 如果原型中还没有,就到原型的原型中查找
  • 如此往复,直到Object.protitype还没有,那么就返回undefined
  • 如果是调用方法就报错,该xxx不是一个函数

闭包

闭包的概念

什么是闭包?
闭包的含义就是闭合,包起来,简单的来说,就是一个具有封闭功能与包裹功能的结构。所谓的闭包就是一个具有封闭的对外不公开的,包裹结构,或空间。

为什么函数可以构成闭包?
闭包就是一个具有封闭与包裹功能的结构,是为了实现具有私有访问空间的函数的。函数可以构成闭包。函数内部定义的数据函数外部无法访问,即函数具有封闭性;函数可以封装代码即具有包裹性,所以函数可以构成闭包。

闭包有什么用

闭包不允许外部访问,要解决的问题就是让外部间接访问函数内部的数据。事实上,通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提升代码的执行效率。利用闭包可以实现如下需求:

  1. 匿名自执行函数
    一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。
  2. 缓存
    闭包正是可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。
  3. 实现封装
  4. 模拟面向对象的代码风格

闭包的基本模型

  1. 对象模式
    函数内部定义个一个对象,对象中绑定多个函数(方法),返回对象,利用对象的方法访问函数内的数据
  2. 函数模式
    函数内部定义一个新函数,返回新函数,用新函数获得函数内的数据
  3. 沙箱模式
    沙箱模式就是一个自调用函数,代码写到函数中一样会执行,但是不会与外界有任何的影响

闭包的作用举例

  1. 获得超过一个数据,返回一个对象
  2. 完成读取一个数据和修改这个数据,对象里面的get\set方法

闭包的性能问题

函数执行需要内存,那么函数中定义的变量,会在函数执行结束后自动回收,凡是因为闭包结构的,被引出的数据,如果还有变量引用这些数据的话,那么这些数据就不会被回收。因此在使用闭包的时候如果不使用某些数据了,一定要赋值一个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这种语法糖。

好了,问自己两个问题:

  1. 为什么要使用原型?——提高函数的复用性。
  2. 为什么属性不放在原型上而方法要放在原型上?
    • 利用对象的动态特性:构造函数.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();

原型链结构图为:

使用原型需要注意两点:

  1. 原型继承链条不要太长,否则会出现效率问题。
  2. 指定原型时,注意constructor也会改变。

继承

实现继承有两种常见方式:

  1. 混合式继承:最简单的继承就是将别的对象的属性强加到我身上,那么我就有这个成员了。
    混合式继承的简单描述:

    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( ‘我吃饱了‘ ); }
    });
  2. 原型继承:利用原型也可以实现继承,不需要在我身上添加任何成员,只要原型有了我就有了。
  3. 借用构造函数继承
    这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数,而函数只不过是在特定环境中执行代码的对象,因此通过使用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 而言,构造函数与方法没有本质的区别。

特征

  1. 使用 new 关键字,来引导构造函数。
  2. 构造函数中的 this 与方法中的一样,表示对象,但是构造函数中的对象是刚刚创建出来的对象
  3. 构造函数中不需要 return ,就会默认的 return this。
    • 如果手动添加return ,就相当于 return this
    • 如果手动的添加 return 基本类型,无效,还是保留原来 返回 this
    • 如果手动添加的 return null,或 return undefined ,无效
    • 如果手动添加 return 对象类型,那么原来创建的 this 就会被丢掉,返回的是 return 后面的对象

创建对象的模式

  1. 工厂方法,工厂就是用来生产的,因此如果函数创建对象并返回,就称该函数为工厂函数
  2. 构造方法
  3. 寄生式创建
  4. 混合式创建

上下文调用模式

上下文就是环境。就是自己定义设置 this 的含义。

语法

  1. 函数名.apply( 对象, [ 参数 ] );
  2. 函数名.call( 对象, 参数 );

描述

  1. 函数名就是表示函数本身,使用函数进行调用的时候默认 this 是全局变量
  2. 函数名也可以是方法提供,使用方法调用的时候,this 是指向当前对象
  3. 使用 apply 进行调用后,无论是函数还是方法都无效了,我们的 this ,由 apply 的第一个参数决定

参数问题
无论是 call 还是 apply 在没有后面的参数的情况下(函数无参数,方法五参数)是完全一致的

function foo(){
    console.log( this );
}
foo.apply( obj );
foo.call( obj );

第一个参数的使用也是有规则的:

  1. 如果传入的是一个对象,那么就相当于设置该函数中的 this 为参数
  2. 如果不传入参数,或传入 null 、undefined 等,那么相当于 this 默认为 window
    foo();
    foo.apply();
    foo.apply( null );
    foo.call( undefined );
  3. 如果传入的是基本类型,那么 this 就是基本类型对应的包装类型的引用

在使用上下文调用的时候,原函数(方法)可能会带有参数,那么这个参数再上下文调用中使用 第二个(第 n 个)参数来表示

function foo( num ) {
    console.log( num );
}
foo.apply( null, [ 123 ] );
// 等价于
foo( 123 );


参考资料

作者: Dunizb 
链接:http://www.imooc.com/article/13649
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作!

时间: 2024-11-05 06:07:27

JavaScript面向对象核心知识归纳的相关文章

JavaScript 面向对象开发知识基础总结

JavaScript 面向对象开发知识基础总结 最近看了两本书,书中有些内容对自己还是很新的,有些内容是之前自己理解不够深的,所以拿出来总结一下,这两本书的名字如下: JavaScript 面向对象精要 JavaScript 启示录 如果对于 JavaScript 面向对象编程理解不够深的话,第一本书还是强烈推荐的.第二本书比较适合初中级的开发者阅读.对各种知识点都有代码示例.内容中规中矩. 1.JavaScript 中的变量类型和类型检测 C#和Java等编程语言用栈存储原始类型,用堆存储引用

javascript的基础知识及面向对象和原型属性

自己总结一下javascript的基础知识,希望对大家有用,也希望大家来拍砖,毕竟是个人的理解啊 1.1 类型检查:typeof(验证数据类型是:string) var num = 123; console.log(typeof num); // 1.2 in 运算符 作用:判断指定属性是否存在于指定的对象中. 如果指定的属性存在于指定的对象中,则 in 运算符会返回 true. 语法: 属性 in 对象 返回值:true 或者 false 示例: var obj = { age: 18 };

JavaScript面向对象及相关知识

最近在学习JavaScript面向对象,写下自己的理解及相关资料,以备查阅. 一.面向对象中涉及的相关几个概念 1.作用域 所谓作用域,就是说属性和函数的可访问范围.在JavaScript中,作用域分为两种.全局作用域和局部作用域. 所有没有var 声明 或 定义于最外层函数和最外层函数外面即为全局作用域.也就是定义可以随意调用. 自定义函数内部用var声明的为局部作用域. var num = 1; //全局作用域 window.onload = function() { //最外层为全局作用域

JavaScript(核心、BOM、DOM)

http://www.flyne.org/article/407 JavaScript(核心.BOM.DOM) JavaScript是基于对象和事件驱动的客户端脚本语言.有如下特点: 交互性 安全性(不可以直接访问本地硬盘) 跨平台性(只要是可以解析js的浏览器都可以执行,和平台无关) 1.JavaScript和Java不同! ①所属:NetScape和Sun ②基于对象和面向对象 ③JS可以直接解析执行,Java需先编译在运行 ④JavaScript是一种弱类型的语言,Java是强类型的语言.

javascript面向对象创建高级 Web 应用程序

 目录 JavaScript 对象是词典 JavaScript 函数是最棒的 构造函数而不是类 原型 静态属性和方法 闭包 模拟私有属性 从类继承 模拟命名空间 应当这样编写 JavaScript 代码吗? 展望 最近,我面试了一个有五年 Web 应用程序开发经验的软件开发人员.四年半来她一直在从事 JavaScript 相关的工作,她自认为 JavaScript 技能非常好,但在不久之后我就发现实际上她对 JavaScript 知之甚少.话虽这样说,但我确实没有责备她的意思.JavaScrip

javascript面向对象之this指针

下午用面向对象的方法写一个幻灯片插件的时候不小心写出了这样的代码: Slider.prototype.auto=function() { setInterval(this.toRun,4000);//注意 } Slider.prototype.toRun=function() { if(this.iNow==this.aA.length - 1) ...... } 在浏览器打开的时候发现幻灯片不能如预期般自动切换,控制台给出了这样的错误提示: this.aA isundefined?然而我已经在

《Javascript面向对象精要》笔记

刚读过<Javascript面向对象精要>这本书,在现有的知识体系里面有一些新鲜的认识,记录一下. 原始类型和引用类型 Javascript存在两种类型:原始类型和引用类型.原始类型包括String.Number.Boolean.Null.Undefined,引用类型保存对象,其本质是对象所在内存位置的引用. 原始类型的赋值或者给函数传参,实际上都是传递原始类型值的拷贝: 引用类型则是引用的拷贝.修改其中一个引用的话,其他引用也会受到影响.如果对象中的某个属性也是对象,在对象拷贝时就会引入深拷

网络基础知识、ASP.NET 核心知识(1)*

为什么要写网络? 我原本的计划是这样的,连续两天梳理ASP.NET开发的核心知识.说到这呢,有人问了.“不是说好了做ASP.NET笔记吗?为啥要写网络基础知识?是不是傻?” 原因是这样的.作为网站开发人员,你所开发的软件产品最终是要在网络上运行的.这就像一个生产商,要生产供给东北地区的产品,而生产商对东北的天气.地理.人文毫无了解.生产商的产品肯定是不可用的,或者低端的!产品质量不好,那是自然的.产品质量好,那算你点儿高.那么作为ASP.NET开发人员,如果一点不懂http协议.不晓得Ajax.

javascript面向对象一

从今天开始就写自己学习的东西了,有点小激动.(刚给妈妈按摩完脚,手指头有点不好使)好了,废话不说了,今学的是JS中面向对象的知识. 面向对象:就是用对象的思想去写代码,而我们通常写JS时是过程式的写法.何为对象?其实在原生JS中有许多的系统对象,比如Date,Array,Json(object)等. 面向对象编程(OOP)的特点:1. 抽象:抓住核心问题;2. 封装:只能通过对象来访问方法;3. 继承:从已有对象上继承出新的对象:4. 多态:多对象的不同形态. 对象的组成:属性+方法. 下面来说