JavaScript面向对象中心学问归结

一、面向对象

1.1 观点

  1. 面向对象就是运用对象。面向对象开辟就是运用对象开辟。
  2. 面向历程就是用历程的体式格局举行开辟。面向对象是对面向历程的封装。

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 ];
    }
}

1.3 关于面向对象的一些其他观点

类class:在JS中就是组织函数

  • 在传统的面向对象言语中,运用一个叫类的东西定义模板,然后运用模板建立对象。
  • 在组织要领中也具有相似的功用,因而也称其为类

实例(instance)与对象(object)

  • 实例平常是指某一个组织函数建立出来的对象,我们称为XXXX 组织函数的实例
  • 实例就是对象。对象是一个泛称
  • 实例与对象是一个近义词

键值对与属性和要领

  • 在JS中键值对的鸠合称为对象
  • 假如值为数据(非函数),就称该键值对为属性
  • 假如值为函数(要领),就称该键值对为要领method

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

  • 传统的面向对象言语中运用类来完成继承那末就有父类、子类的观点
  • 父类又称为基类,子类又称为派生类
  • 在JS中没有类的观点,在JS中经常称为父对象,子对象,基对象,派生对象。

二、组织函数

2.1 组织函数是干什么用的

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

2.2 建立对象的历程

  1. 代码:var p = new Person();
  2. 起首运算符new建立了一个对象,相似于{},是一个没有任何(自定义)成员的对象。

    • 运用new 建立对象,那末对象的范例就是建立他的组织函数名
    • 运用{}不管怎样都是Object范例,相称于new Object
  3. 然后挪用组织函数,为其初始化成员

    • 组织函数在挪用的一开始,有一个赋值操纵,即this = 方才建立出来的对象。
    • 因而在组织函数中this示意方才建立出来的对象。
  4. 在组织函数中 应用 对象的动态特性 为其对象增添成员。

三、作用域

3.1 什么是作用域

域示意的就是局限,即作用域,就是一个名字在什么地方可以运用,什么时刻不能运用。
简朴的说,作用域是针对变量的,比方我们建立一个函数 a1,函数内里又包了一个子函数 a2

// 全局作用域
functiona a1() {
    // a1作用域
    function a2() {
        // a2作用域
    }
}

此时就存 在三个作用域:全局作用域,a1 作用域,a2 作用域;即全局作用域包含了 a1 的作用域,a2 的作用域包含了 a1 的作用域。

a2 在查找变量的时刻会先从自身的作用域区查找,找不到再到上一级 a1 的作用域查找,假如还没找到就
到全局作用域区查找,如许就形成了一个作用域链

3.2 JS中词法作用域的划定规矩

  1. 函数许可接见函数外部的数据
  2. 全部代码构造中只需函数可以限制作用域
  3. 作用划定规矩起首运用提拔划定规矩剖析
  4. 假如当前作用域中有了名字了,就不斟酌表面的名字

3.3 属性搜刮准绳

  • 所谓的属性搜刮准绳,就是对象在接见属性或要领的时刻,起首在当前对象中查找
  • 假如当前对象中存储着属性或要领,住手查找,直接运用该属性或要领
  • 假如当前对象没有该成员,那末再在其原型对象中查找
  • 假如原型对象中含有该成员,那末住手查找,直接运用
  • 假如原型中还没有,就到原型的原型中查找
  • 云云来去,直到Object.protitype还没有,那末就返回undefined
  • 假如是挪用要领就报错,该xxx不是一个函数

四、闭包

4.1 说说你对闭包的明白

有用闭包重要是为了设想私有要领和变量。闭包的长处是可以防止全局变量的污染;瑕玷是闭包会常驻内存,增添内存运用量,运用不当很轻易形成内存泄漏。在JavaScript中,函数即闭包,只需函数才发作作用域。

闭包有3个特性:

  1. 函数嵌套函数
  2. 在函数内部可以援用外部的参数和变量
  3. 参数和变量不会以渣滓接纳机制接纳

4.2 闭包有什么用(特性)

闭包的作用,就是保存自身私有的变量,经由过程供应的接口(要领)给外部运用,但外部不能直接接见该变量。
经由过程运用闭包,我们可以做很多事变,比方模仿面向对象的代码作风;更文雅,更简约的表达出代码;在某些方面提拔代码的实行效力。应用闭包可以完成以下需求:

  1. 匿名自实行函数

一个匿名的函数,并马上实行它,由于外部没法援用它内部的变量,因而在实行完后很快就会被开释,关键是这类机制不会污染全局对象。

  1. 缓存

闭包恰是可以做到这一点,由于它不会开释外部的援用,从而函数内部的值可以得以保存。

  1. 完成封装
  2. 模仿面向对象的代码作风

4.3 闭包的基本模子

对象情势
函数内部定义个一个对象,对象中绑定多个函数(要领),返回对象,应用对象的要领接见函数内的数据

function createPerson() {
    var __name__ = "";
    return {
        getName: function () {
            return __name__;
        },
        setName: function( value ) {
            // 假如不姓张就报错
            if ( value.charAt(0) === '张' ) {
                __name__ = value;
            } else {
                throw new Error( '姓氏不对,不能取名' );
            }
        }
    }
}
var p = createPerson();
p.set_Name( '张三丰' );
console.log( p.get_Name() );
p.set_Name( '张王繁华' );
console.log( p.get_Name() );

函数情势
函数内部定义一个新函数,返回新函数,用新函数取得函数内的数据

function foo() {
    var num = Math.random();
    function func() {
        return mun;
    }
    return func;
}
var f = foo();
// f 可以直接接见这个 num
var res1 = f();
var res2 = f();

沙箱情势
沙箱情势就是一个自挪用函数,代码写到函数中一样会实行,然则不会与外界有任何的影响,比方jQuery

(function () {
   var jQuery = function () { // 一切的算法 }
   // .... // .... jQuery.each = function () {}
   window.jQuery = window.$ = jQuery;
})();
$.each( ... )

4.4 闭包的机能题目

js 渣滓接纳机制,也就是当一个函数被实行完后,其作用域会被收回,假如形成了闭包,实行完后其作用域就不会被收回。

函数实行须要内存,那末函数中定义的变量,会在函数实行完毕后自动接纳,通常由于闭包构造的,被引出的数据,假如另有变量援用这些数据的话,那末这些数据就不会被接纳。因而在运用闭包的时刻假如不运用某些数据了,一定要赋值一个null

var f = (function () {
    var num = 123;
    return function () {
        return num;
    };
})();
// f 援用着函数,函数援用着变量num
// 因而在不运用该数据的时刻,最好写上
f = null;

五、原型

5.1 什么是原型

一句话申明什么是原型:原型能存储我们的要领,组织函数建立出来的实例对象可以援用原型中的要领。

JS中一切皆对象,而每一个对象都有一个原型(Object除外),这个原型,也许就像Java中的父类,所以,基本上你可以以为原型就是这个对象的父对象,即每一个对象(Object除外)内部都保存了它自身的父对象,这个父对象就是原型。平常建立的对象假如没有迥殊指定原型,那末它的原型就是Object(这就很相似Java中一切的类默许继承自Object类)。

ES6经由过程引入class ,extends等关键字,以一种语法糖的情势把组织函数包装成类的观点,更便于人人明白。是愿望开辟者不再花精神去关注原型以及原型链,也充分申明原型的设想企图和类是一样的。

5.2 检察对象的原型

当对象被建立以后,检察它们的原型的要领不止一种,之前平常运用对象的__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引荐的检察原型和设置原型的要领。

5.3 原型的用法

实在原型和类的继承的用法是一致的:当你想用某个对象的属性时,将当前对象的原型指向该对象,你就具有了该对象的运用权了。

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引入classextends 来掩饰原型的观点也是一个很友爱的行为,关于历久进修那些类继承为基本的面临对象编程言语的程序员而言。

我的发起是,尽量明白原型,尽量用class这类语法糖。

好了,问自身两个题目:

  1. 为何要运用原型?——进步函数的复用性。
  2. 为何属性不放在原型上而要领要放在原型上?

    • 应用对象的动态特性:组织函数.prototype.xxxx = vvv
    • 应用直接替代
    Student.prototype = {
    sayHello : function(){},
    study : function(){}
    };

5.4 原型链

什么是原型链?
通常对象就有原型,那末原型又是对象,因而通常给定一个对象,那末就可以找到他的原型,原型另有原型,那末云云下去,就组成一个对象的序列,称该构造为原型链。

每一个实例对象都有一个
__proto_属性,该属性指向它原型对象,这个实例对象 的组织函数有一个原型属性
prototype,与实例的
__proto__属性指向同一个对象。当一个对象在查找一个属性的时, 自身没有就会依据
__proto__ 向它的原型举行查找,假如都没有,则向它的原型的原型继承查找,直到查到
Object.prototype._proto_
null,如许也就形成了
原型链

这个观点实在也变得比较简朴,可以类比类的继承链条,即每一个对象的原型往上追溯,一直到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();

原型链构造图为:
《JavaScript面向对象中心学问归结》

运用原型须要注重两点:

  1. 原型继承链条不要太长,不然会涌现效力题目。
  2. 指定原型时,注重constructor也会转变。

六、继承

完成继承有两种罕见体式格局:

6.1 混合式继承

最简朴的继承就是将别的对象的属性强加到我身上,那末我就有这个成员了。
混合式继承的简朴形貌:

function Person() {};
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( '我吃饱了' ); }
});

6.2 原型继承

应用原型也可以完成继承,不须要在我身上增添任何成员,只需原型有了我就有了。

6.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种体式格局

七、函数的四种挪用情势

7.1 函数情势

就是一个简朴的函数挪用。函数名的前面没有任何指导内容。

function foo () {}
var func = function () {};
...
foo();
func();
(function () {} )();

this 的寄义:在函数中 this 示意全局对象,在浏览器中式 window

7.2 要领情势

要领一定式倚赖与一个对象,将函数赋值给对象的一个属性,那末就成为了要领。

function f() {
    this.method = function () {};
}
var o = {
    method: function () {}
}

this 的寄义:这个倚赖的对象

7.3 组织器挪用情势

建立对象的时刻组织函数做了什么?由于组织函数只是给 this 增添成员,没有做其他事变。而要领也可以完成这个操纵,就是 this 而言,组织函数与要领没有实质的区分。

特性:

  1. 运用 new 关键字,来指导组织函数。
  2. 组织函数中的 this 与要领中的一样,示意对象,然则组织函数中的对象是方才建立出来的对象
  3. 组织函数中不须要 return ,就会默许的 return this

    • 假如手动增添return ,就相称于 return this
    • 假如手动的增添 return 基本范例,无效,照样保存本来 返回 this
    • 假如手动增添的 return null,或 return undefined ,无效
    • 假如手动增添 return 对象范例,那末本来建立的 this 就会被丢掉,返回的是 return 背面的对象

7.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 );

第一个参数的运用也是有划定规矩的:

  • 假如传入的是一个对象,那末就相称于设置该函数中的 this 为参数
  • 假如不传入参数,或传入 nullundefined 等,那末相称于 this 默以为 window
foo();
foo.apply();
foo.apply( null );
  • 假如传入的是基本范例,那末 this 就是基本范例对应的包装范例的援用

在运用上下文挪用的时刻,原函数(要领)能够会带有参数,那末这个参数再上下文挪用中运用 第二个(第 n 个)参数来示意

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

参考资料

    原文作者:dunizb
    原文地址: https://segmentfault.com/a/1190000007269204
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞