JavaScript的面向对象

对象属性的得到和安装

能够由此点(.)或方括号([])运算符来获取和设置属性的值。

var author = book.author;
var title = book["main title"];

在JavaScript中能用 . 连接的都足以用 []总是。有非常多 . 运算符不可能用的时候,就须要用[]代替。
1、在性质名可变的动静下用[]

function getAttr (obj, attr) {
    console.log(obj[attr])
}

2、属性名有空格也许连字符等时用[]

var title = book["main title"];

ES6的面向对象

地方所说的是JavaScript语言的价值观艺术,通过构造函数,定义并生成新的对象。
ES6中提供了更就像是传统语言的写法,引进了Class(类)的定义,通过class关键字,能够定义类。

语法

ES6的类完全能够看作是构造函数的另外一种写法。

var method = 'say';
class Dog {
    constructor (name,color) {
        this.name = name;
        this.color = color;
    }
    //注意,两个属性之间跟对象不同,不要加逗号,并且类的属性名可以使用变量或者表达式,如下
    [method] () {
        console.log('汪汪');
    }
}
console.log(typeof Dog); // function 类的数据类型就是函数
console.log(Dog === Dog.prototype.constructor); // true 类本身就是构造函数

既是是构造函数,所以在采纳的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

var hashiqi = new Dog('hashiqi', 'blackandwhite');
console.log(hashiqi.color); // blackandwhite

//上面采用表达式声明的类的属性可以用一下两种方式调用
hashiqi[method](); // 汪汪
hashiqi.say(); // 汪汪

注意:
1、先注明定义类,再创立实例,不然会报错
class 空头支票变量升高,那或多或少与ES5的构造函数完全两样

new Dog('hashiqi','blackandwhite')
class Dog {
    constructor (name,color) {
        this.name = name;
        this.color = color;
    }
}
//Uncaught ReferenceError: Dog is not defined
//上面代码,Dog类使用在前,定义在后,因为ES6不会把类的声明提升到代码头部,所以报错Dog没有定义。

2、必得选择new关键字来创制类的实例对象
类的构造函数,不选用new是没办法调用的,会报错。 那是它跟平日构造函数的贰个重大不相同,前面一个不用new也能够进行。

class Dog {
    constructor (name,color) {
        this.name = name;
        this.color = color;
    }
}
Dog(); // Uncaught TypeError: Class constructor Dog cannot be invoked without 'new'

3、定义“类”的措施的时候,前边不要求加多function这么些第一字,直接把函数定义放进去了就能够了。并且,方法之间不要求逗号分隔,加了会报错。

属性概念

constructor 构造函数

构造方法constructor是贰个类必供给某些艺术,暗中同意再次来到实例对象;制造类的实例对象的时候,会调用此办法来开始化实例对象。假如你未有编写制定constructor方法,实行的时候也会被加多二个暗许的空的constructor方法。

constructor方法是必得的,也是独一的,三个类体无法含有八个constructor构造方法。

class Dog {
    constructor (name,color) {
        this.name = name;
        this.color = color;
    }
    //定义了两个constructor,所以会报错
    constructor () {

    }
}
new Dog('hashiqi', 'blackandwhite')
//Uncaught SyntaxError: A class may only have one constructor

Class表达式

与函数同样,类能够选用表明式的方式定义。

const Hashiqi = class Dog {
    constructor (name,color) {
        this.name = name;
        this.color = color;
    }
    getName () {
        //此处的Dog就是Dog构造函数,在表达式形式中,只能在构造函数内部使用
        console.log(Dog.name);
    }
}
var hashiqi = new Hashiqi('hashiqi', 'blackandwhite'); // 真正的类名是Hashiqi
var jinmao = new Dog('jinmao', 'yellow'); // 会报错,Dog没有定义

平日我们的表明式会写成如下,省略掉类前边的名目

const Hashiqi = class {
    constructor (name,color) {
        this.name = name;
        this.color = color;
    }
}
var hashiqi = new Hashiqi('hashiqi', 'blackandwhite');

实例方法和静态方法 实例化后的靶子才方可调用的法门叫抓实例方法。 直接使用类名就能够访谈的措施,称之为“静态方法”

类相当于实例的原型,全部在类中定义的法子,都会被实例继承。假诺在贰个办法前,加上static关键字,就表示该方法不会被实例承继,而是直接通过类来调用,那就称为“静态方法”。

class Dog {
    constructor (name,color) {
        this.name = name;
        this.color = color;
    }
    static say () {
        console.log('汪汪');
    }
}
Dog.say(); //汪汪

静态方法和实例方法差别的是:静态方法的概念须要运用static关键字来标志,而实例方法无需;另外,静态方法通过类名来的调用,而实例方法通超过实际例对象来调用。

类的存在延续

extends

类之间能够通过extends关键字贯彻持续,那比ES5的经过修改原型链完毕三番五次,要清晰和造福广大。

class Dog extends Animal{}

extends的后续目的
extends关键字背后能够跟多样类型的值,有两种特别景况

1、子类继承Object类

class A extends Object {}
console.log(A.__proto__ === Object) //true
console.log(A.prototype.__proto__ == Object.prototype) //true
//这种情况下,A其实就是构造函数Object的复制,A的实例就是Object的实例。

2、官样文章继续

class A {}

console.log(A.__proto__ === Function.prototype) // true
console.log(A.prototype.__proto__ === Object.prototype) // true
//这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Funciton.prototype。
//但是,A调用后返回一个空对象(即Object实例),所以A.prototype.__proto__指向构造函数(Object)的prototype属性。

3、子类承继null

class A extends null {}
console.log(A.__proto__ === Function.prototype) //true
console.log(A.prototype) //只有一个constructor属性,没有__proto__属性
这种情况与第二种情况非常像。A也是一个普通函数,所以直接继承Funciton.prototype。
但是,A调用后返回的对象不继承任何方法,所以没有__proto__这属性

super

uper这么些首要字,既可以够看成函数使用,也能够看成对象使用。

1、super作为函数调用时,代表父类的构造函数。作为函数时,super()只可以用在子类的构造函数之中,用在任哪里方就能报错。

2、super作为对象时,在常常方法中,指向父类的原型对象;在静态方法中,指向父类。

class Animal {
    constructor (name) {
        this.name = name;
        this.species = '动物';
    }
    say (){
        return this.species;
    }
}
class Dog extends Animal{
    constructor (name, color) {
        // 只要是自己在子类中定义constructor,必须调用super方法,否则新建实例会报错
        //super作为函数调用,只能用在子类的constructor中
        super(name);
        this.color = color;
    }
    getInfo () {
        //普通方法中,super指向父类的原型对象
        console.log(super.say()+': '+this.name +','+this.color);
    }
}
var hashiqi = new Dog('hashiqi', 'blackandwhite');
hashiqi.getInfo() //动物:hashiqi,balckandwhite

注意:
1、子类必需在constructor方法中调用super方法,不然新建实例时会报错。那是因为子类未有和谐的this对象,而是继续父类的this对象,然后对其进展加工。假使不调用super方法,子类就得不到this对象。

2、在子类的平时方法中,由于super指向父类的原型对象,所以定义在父类实例上的点子或性质,是力不能够及通过super调用的。

3、使用super的时候,必得显式钦定是用作函数、依旧作为对象使用,不然会报错。

原来的小说地址:

打包对象

当使用原始类型的值(string、number、boolean),在调用对应属性和方式的时候,内部会自动转成对应的对象。隐式创制的那个指标,就改为包装对象。
基本类型皆有谈得来相应的卷入对象 : String Number Boolean

包裹对象的特色 隐式创立对象后,能够调用对应的习性和艺术 使用后,立马销毁,所以不能够给原始类型的值增多属性和格局

其进程举例:str.substring - > new String(1234) - > 找到String的substring -> 将new String销毁

面向对象编制程序

通俗点,用对象的记挂写代码正是面向对象编制程序。

基本特征

1、抽象:抓住核心难题(简单明了为收取像的片段;将一样或显示与主题素材相关特征的内容提收取来。)
其主题:抽出、抽离,将同一的有的(恐怕会爱护、会迭代、会扩张)的代码抽离出来形成一类

2、封装:便是将类的质量包装起来,不让外部轻便明白它当中的有血有肉达成;只提供对外接口以供调用

3、承接:从已有对象上此起彼落出新的指标

4、多态:三个目的的不等形态

面向对象的益处

1、代码的等级次序结构更显著
2、更易于复用
3、更易于有限帮忙
4、更易于扩大

面向对象相关的性质和定义

__proto__ 属性原型链,实例对象与原型之间的总是,叫做原型链。

指标身上独有 proto 构造函数身上有prototype也可能有 proto

constructor 重回创制实例对象的构造函数的引用,每一个原型都会自动增添constructor属性,for..in..遍历原型是找不到这么些性子的。

var a = new A();
console.log(a.constructor == A) //true

hasOwnProperty 能够用来剖断某属性是否以此构造函数的中间属性(不包罗继续的)

语法: obj.hasOwnProperty(prop) 返回Boolean

function A (){
    this.b = 1;
}
var a = new A();
console.log(a.hasOwnProperty('b'));  //打印true 
console.log(a.hasOwnProperty('toString')); //toString是继承属性 打印 false
console.log(a.hasOwnProperty('hasOwnProperty')); //同上,打印false

nstanceof 二元运算符,用来检验一个目标在其原型链中是或不是留存五个构造函数的 prototype 属性。

语法: object instanceof constructor 即检查实验 constructor.prototype 是或不是留存于参数 object 的原型链上。

// 定义构造函数
function C(){} 
function D(){} 

var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype不在o的原型链上
o instanceof Object; // true,因为Object.prototype.isPrototypeOf(o)返回true
C.prototype instanceof Object // true,同上

toString 重返二个代表该对象的字符串

作用:
1、实行数字之间的进制转变

例如:var num = 255;
alert( num.toString(16) ); //结果就是'ff'

2、利用toString做项目标剖断

例如:var arr = [];
alert( Object.prototype.toString.call(arr) == '[object Array]' );     弹出true
Object.prototype.toString.call()    得到是类似于'[object Array]'  '[object Object]'

对象方法和性质的聚焦

Object静态方法

  • Object.assign()
  • Object.create()
  • Object.defineProperty()
  • Object.defineProperties()
  • Object.entries()
  • Object.preventExtensions()
  • Object.isExtensible()
  • Object.seal()
  • Object.isSealed()
  • Object.freeze()
  • Object.isFrozen()
  • Object.keys()
  • Object.values()
  • Object.getPrototypeOf()
  • Object.getOwnPropertyNames()
  • Object.getOwnPropertyDescriptor()
  • Object.getOwnPropertyDescriptors()

Object的实例方法(定义在Object.prototype上的)

  • Object.prototype.hasOwnProperty()
  • Object.prototype.isPrototypeOf()
  • Object.prototype.propertyIsEnumerable()
  • Object.prototype.toString()
  • Object.prototype.valueOf()

JavaScript的对象

指标是JavaScript的一种数据类型。对象能够当做是性质的冬日聚焦,每个属性都是三个键值对,属性名是字符串,因而能够把对象看成是从字符串到值的映照。这种数据结构在其他语言中称之为“散列(hash)”、“字典(dictionary)”、“关联数组(associative array)”等。

原型式承继:对象不止是字符串到值的照耀,除了可以保证自有的性质,JavaScript对象还是能够从贰个叫做原型的目的承接属性,对象的法子常常是三回九转的属性,那是JavaScript的主干特征。

JavaScript对象是动态的—可以激增属性也足以去除属性,不过他们常用来模拟静态以至静态类型语言中的“结构体”

编码观念

三种编程格局:
(1)、面向进度
(2)、面向对象

两个的界别:
面向进程:关切达成进度和每一步的贯彻细节。
面向对象:关Whyet征和成效。

面向对象的写法历程

1、原始情势

设若我们有一个指标是狗的原型,这么些原型有“名字”和“颜色”五个属性。

var Dog = {
name: '',
color: ''
}
据说那几个原型对象,大家要生成三个实例对象如下

var hashiqi = {}; //创建空对象,之后根据原型对象的相应属性赋值
hashiqi.name = 'hashiqi';
hashiqi.color = 'blackandwhite';

缺点:
1、倘诺要生成四个实例对象,要双重写多次。
2、实例和原型之间未有交换。

2、工厂格局

位置原始方式有贰个瑕玷是要很费劲的写过多双重的代码,大家能够写三个函数来消除代码重复的标题。

function Dog(name, color) {
    var obj = {};
    obj.name = name;
    obj.color = color;
    return obj;
}

var hashiqi = Dog('hashiqi', 'blackandwhite');
var jinmao = Dog('jinmao', 'yellow');

这种情势只是消除了代码重复的主题材料,不过变化的实例跟原型依然不曾关联,并且hashiqi和jinmao也尚无关系,不可能展现出他们是同多少个原型对象的实例。

3、构造函数格局

用来创造对象的函数,叫做构造函数,其实正是一个平日函数,不过暗中认可函数名首字母大写,对构造函数使用new运算符,就会生成实例,何况this变量会绑定在实例对象上。

function Dog(name, color) {
    this.name = name;
    this.color = color;
}

var hashiqi = new Dog('hashiqi', 'blackandwhite');
var jinmao = new Dog('jinmao', 'yellow');
console.log(hashiqi.name); //hashiqi
console.log(jinmao.name); //jinmao

hasiqi 和 jinmao有三个贰只的构造函数 hashiqi.constructor === jinmao.constructor 是true

有以下三种办法可以作证原型对象与实例对象的关系:

hashiqi instanceof Dog; // true

Object.getPrototypeOf(hashiqi) === Dog.prototype // true

Dog.prototype.isPrototypeOf(hashiqi) // true

缺点:
构造函数化解了代码重复和实例与原型之间的维系,不过存在贰个荒疏内部存款和储蓄器的标题。譬喻远行对象有局地不改变的习性和通用的主意,这样没生成一个实例,都不可能不为重新的事物多占一些内部存款和储蓄器。

扩展

我们得以品味完毕new运算符的逻辑如下:

function New(func) {
    var obj = {};

    //判断构造函数是否存在原型,如果有实例的__proto__属性就指向构造函数的prototype
    if(func.prototype !== undefined) {
        obj.__proto__ = func.prototype;
    }

    // 模拟出构造函数内部this指向实例的过程,注意,我们会拿到构造函数的返回值
    var res = func.apply(obj, Array.from(arguments).slice(1));

    // 正常构造函数是不需要显式声明返回值的,默认的返回值是生成的实例,但是一旦在构造函数中return 一个不是对象或者函数,就会改变构造函数的默认的返回值,其他的类型是不变的
    if(typeof res === 'object' && res !== null || typeof res === 'function') {
        return res;
    }

    return obj;
}

var taidi = New(Dog, 'taidi', 'gray');

注意:
不奇怪的构造函数是没有供给团结写return 的,假使写了,当return的时候,假若是末端为简便类型,那么再次回到值还是构造函数生成的实例。假使return为指标类型大概函数,那么再次来到的正是return前边的那些指标大概函数。

4、prototype模式

每多个构造函数都有 prototype 属性,这么些特性指向的是八个对象,那些指标的享有属性和情势,都会被构造函数的实例承继。
依赖那脾个性,大家就能够有选拔性的将一部分通用的属性和情势定义到 prototype 上,每三个由此 new 生成的实例,都会有八个 proto 属性指向构造函数的原型即 prototype ,那样咱们定义到构造函数原型对象的性质和方法,就能被每贰个实例访问到,从而成为公用的习性和办法。

function Dog(name, color) {
    this.name = name;
    this.color = color;
}
Dog.prototype.say = function () {
    console.log("汪汪");
}

var hashiqi = new Dog('hashiqi', 'blackandwhite');
var jinmao = new Dog('jinmao', 'yellow');

hashiqi.say(); // 汪汪
jinmao.say(); // 汪汪
console.log(hashiqi.say === jinmao.say); // true

小心:当实例对象和原型对象有同样的习性恐怕措施时,会预先访问实例对象的本性或措施。

面向对象的接轨

1、构造函数内部的特性和方法传承

运用call或apply方法,将父对象的构造函数绑定在子对象上。

//父类
function Animal() {
    this.species = '动物';
}

//子类
function Dog(name, color) {
    Animal.call(this);
    this.name = name;
    this.color = color;
}

var hashiqi = new Dog('hashiqi', 'blackandwhite');
console.log(hashiqi.species); //动物

2、prototype相关的后续

子类的prototype指向父类生成实例

function Animal() {};
Animal.prototype.species = '动物';
function Dog(name, color) {
    this.name = name;
    this.color = color;
}
Dog.prototype = new Animal();
//只要是prototype被完全覆盖,都得重写constructor。
Dog.prototype.constructor = Dog;
var hashiqi = new Dog('hashiqi', 'blackandwhite');

缺欠: 每三次卫冕都得生成三个父类实例,相比较占内部存款和储蓄器。

使用空对象作为中介

function Animal() {}
Animal.prototype.species = '动物';
function Dog(name, color) {
    this.name = name;
    this.color = color;
}
//Middle生成的是空实例(除了__proto__),几乎不占内存
function Middle() {}
Middle.prototype = Animal.prototype;
Dog.prototype = new Middle();
Dog.prototype.constructor = Dog;
var hashiqi = new Dog('hashiqi', 'blackandwhite');
console.log(hashiqi.species);

多少个月前在 CSDN 面试的时候,小编说了这种持续格局,面试官就纠缠那样修改子类的prototype不会影响父类么?是的确不会潜移暗化的,因为子类的prototype是指向Middle构造函数生成的实例,即使实在有心要改,得Dog.prototype.__proto__这么着来改。

Object.create()

function Animal() {}
Animal.prototype.species = '动物';
function Dog(name, color) {
    this.name = name;
    this.color = color;
}
Dog.prototype = Object.create(Animal.prototype,{
    constructor: {
        value: Dog
    }
})

var hashiqi = new Dog('hashiqi','blackandwhite');
console.log(hashiqi.species); //动物

3、拷贝承袭

浅拷贝

function Animal() {}
Animal.prototype.species = '动物';
function Dog(name, color) {
    this.name = name;
    this.color = color;
}
function extend(child, parent) {
    var c = child.prototype;
    var p = parent.prototype;
    for(key in p) {
        c[key] = p[key]
    }
}
extend(Dog, Animal);
var hashiqi = new Dog('hashiqi', 'blackandwhite');
console.log(hashiqi.species) // 动物

深拷贝

function deepCopy(parent, child) {
    var child = child || {};
    for(key in parent) {
        if(typeof parent[key] === 'object') {
            child[key] = parent[key].constructor === Array?[]:{};
            deepCopy(parent[key],child[key])
        } else {
            child[key] = parent[key];
        }
    }
    return child;
}

删除属性

delete运算符能够去除对象的属性。
delete只是断开属性和宿主对象的牵连,而不会去操作属性中的属性,要是除去的属性是个对象,那么这么些目的的援用还是存在的。

var a = {b:{c:1}};
var b = a.b;
console.log(b.c); // 1
console.log(a.b); // {c:1}
delete a.b;
console.log(b.c); // 1
console.log(a.b); //undefined

delete只可以删除自有质量,不可能去除传承属性。

返回值

重临值为true

当delete表明式删除成功或从不其他副作用(举个例子删除不设有的品质),恐怕delete后不是贰性格情访谈表明式,delete会再次回到true ;

var a = {b:{c:1}};
console.log(delete a.b);
console.log(delete a.b);
console.log(delete a.toString);
console.log(delete 1);

以上都会打印true

再次回到值为false

delete无法去除那么些可配置性为false的习性,举个例子有个别内置对象的个性是不行配置的,通过变量申明和函数注脚成立的全局对象的属性。

var a = {};
Object.defineProperty(a,'b',{
    value:1,
    configurable: false // 设置为不可配置
})
console.log(delete a.b)
console.log(delete Object.prototype)
var x = 1;
console.log(delete this.x);
console.log(delete x)

以上打印都为false

面向对象

检查评定属性

in 运算符

in 运算符的右边是属性名(字符串),右边是指标。借使指标的自有质量或三番五次属性中蕴藏那性情情则赶回true。

var a = {b:1};
console.log('a' in window); // true 声明的全局变量'a'是window的属性
console.log('b' in a); // true 'b'是a的属性
console.log('toString' in a); // true a继承了toString属性
console.log('c' in a); // false 'c'不是a的属性

跟in运算符类似的,还能用"!=="推断两本天性是或不是是undefined,可是有一种现象只可以选取in运算符,in可以分别不设有的性质和存在但值为undefined的习性。

var a = {b:undefined};
console.log(a.b !== undefined); //false
console.log(a.c !== undefined); //false
console.log('b' in a); //true
console.log('c' in a); //false

hasOwnProperty

指标的hasOwnProperty()方法用来检查测量检验给定的名字是不是是对象的自有质量。对于屡次三番属性它将回来false

var a = {b:1};
console.log(a.hasOwnProperty('b')); //true
console.log(a.hasOwnProperty('c')); //false
console.log(a.hasOwnProperty('toString')); //false toString是继承属性

propertyIsEnumerable

目的的propertyIsEnumerable()方法唯有检查评定到是自己性质(不满含继续的习性)且这么些天性的可枚举性为true时它才回到true。

var a = {b:1};
console.log(a.propertyIsEnumerable('b'));
console.log(a.propertyIsEnumerable('toString'));

创设对象

1、对象直接量

成立对象最简便的艺术便是在JavaScript代码中央银行使对象直接量。

var book = {
            "main title": 'guide',  //属性名字里有空格,必须加引号
            "sub-title": 'JS',  //属性名字里有连字符,必须加引号
            for: 'development',  //for是关键字,不过从ES5开始,作为属性名关键字和保留字可以不加引号
            author: {
                firstname: 'David',  //这里的属性名就都没有引号
                surname: 'Flanagan'
            }
        }

注意: 从ES5初始,对象直接量中的最终贰天性子后的逗号将被忽略。

扩展: [JavaScript中的关键字和保留字]

2、通过new创设对象

new 运算符创造并早先化贰个新对象。关键字new后跟二个函数调用。这里的函数称做构造函数(constructor),构造函数用以开端化三个新创建的目的。JavaScript中的数据类型都包含内置的构造函数。

var o = new Object(); //创建一个空对象,和{}一样。 
var arr = new Array(); //创建一个空数组,和[]一样。

扩展 1:new

new 是一个一元运算符,特意运算函数的。new后边调用的函数叫做构造函数,构造函数new的长河叫加强例化。
当new去调用一个函数 : 今年函数中的this就本着创制出来的对象,而且函数的的重返值间接正是this(隐式再次回到)
有一个暗中认可惯例正是构造函数的名字首字母大写。

注意:
当return的时候,假诺是背后为简易类型,那么再次来到值仍旧这些目的;
如若return为对象类型,那么重临的便是return前边的那一个目的。

强大 2:基本项目和对象类型(复杂类型)的分别

赋值:
骨干项目 : 赋值的时候只是值的复制
对象类型 : 赋值不独有是值的复制,而且也是援用的传递(可见为内部存款和储蓄器地址)能够领略为赋址。

比较相等
大旨类型 : 值一样就能够
对象类型 : 值和引用都无差别才行

扩展 3:原型 prototype

每三个JavaScript对象(null除了这么些之外)都和另一个指标相关联,那些指标正是原型,每三个对象都从原型承接属性。

3、Object.create()

Object.create() 这一个艺术是ES5概念的,它创立八个新目的,在那之中第二个参数是以此目的的原型。第三个参数是可选参数,用以对指标属性进行更上一层楼描述。

能够经过传播参数 null 创制一个不曾原型的新指标,可是这么些新目的不会持继续留任何事物,以至不富含基础措施。
var o = Object.create(null); //o不会继续任何性质和章程,空空的。

只要想成立二个普通的空对象,需求传入Object.prototype
var o = Object.create(Object.prototype); //o相当于{}

本文由银河网址发布于银河网址,转载请注明出处:JavaScript的面向对象

您可能还会对下面的文章感兴趣: