Skip to the content.

面向对象


[TOC]

定义

ECMA是基于原型的实现的面向对象编程

传统方法

function Car(color,logo){
    let obj = {};
    obj.color = color;
    obj.logo = logo;
    return obj;
}
let che = Car('red','BM');
//Object {color: "red", logo: 'BM'}

构造函数方法

构造函数:通过这个函数创建对象,该函数也是一个普通函数,默认约定:函数名首字母大写 通过new+构造函数的方法创建的就相当于一个类

如数组的创建:new Array(); 类似的new Car();用构造函数Car创建对象

function Car(color,logo){
    this.color = color;
    this.logo = logo;
}
let che = new Car('red','BM');
//Car {color: "red", logo: 'BM'}

let che2 = new Car();//{}
let che3 = new Car;//{}

创建空对象可以不用写括号。

ele.constructor

返回创建ele的构造函数 构造函数原型prototype下有一个属性constructor constructor的值指向拥有这个原型的函数 原型重新赋了一个值后,会被改写,需要手动的把constructor改回来。

function Car(color,logo){
    this.color = color;
    this.logo = logo;
}
let che = new Car('red','BM');
console.log(che.constructor)
/*
ƒ Car(color,logo){
	this.color = color;
	this.logo = logo;
}
*/

如果构造函数中有return,后面的值为简单数字/字符串等,不起作用,但如果是一个数组/对象的话,返回的就是这个新添加的对象,本应返回的对象没了,主要看return后的类型是否是对象

prototype

如果像数组一样,都共享一个push方法,那么下面的这种方法就比较浪费了,因为每创建一个对象,就给它赋值一个方法run,会浪费内存,并不是共享

Javascript规定,每一个构造函数都有一个prototype属性,指向一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承

function Car(color,logo){
    this.color = color;
    this.logo = logo;
    this.run = function(){
        alert('奔跑吧!')
    }
}

let che = new Car('red','BM');
let che2 = new Car('white','BM');

console.log( che.run === che2.run ) //false

把一类对象共享的属性和方法,放在原型prototype上 每一个函数都拥有一个属性prototype,就是原型,所有的共享的属性和方法都在prototype上。

原型对应的值是一个对象,默认是通过Object创建的

function Car(color,logo){
    this.color = color;
    this.logo = logo;
}

Car.prototype.run = function(){
    alert('奔跑吧!')
}

let che = new Car('red','BM');
let che2 = new Car('white','BM');

console.log( che.run === che2.run ) //true

当一个对象去查找一个属性的时候,先从自身开始,如果自身有的话,就取自身的。如果自身没有,就找构造函数的原型,如果构造函数原型再没有,继续往上找,直到Object.prototype为止,再没有,返回undefined

从自身找完没有的话,去找构造函数的prototype

let aa = {};

Object.abc = 123;
Object.prototype.abc = 456;
console.log(aa.abc)//456
//aa没有,会往上找Object.prototype看是否有属性abc,并不会去找Object上的属性abc,

__proto__

每个对象都有一个属性__proto__,指向创建该对象的构造函数的原型prototype.

如果改变一个对象的__proto__,只会影响此对象找构造函数原型,并不会改变构造函数原型的prototype,通过构造函数创建的对象依然可以调用原型方法。

如果在对象的__proto__上增加一个属性,也就是在构造函数原型上添加了一个属性,后续通过构造函数创建的对象可以访问到该属性。并不像上面整体改变后原型不变的状态

let arr = [];
console.log( arr.__proto__ === Array.prototype )//true

arr.__proto__ = {a:1}
console.dir(arr.__proto__)//{a:1}
console.dir(Array.prototype)//并没有改变

即使构造函数上有abc属性,有她创建的对象寻找属性时,找不到也不会从它身上找,而是会从构造函数的prototype上找,因为对象身上的__proto__指向的是他的构造函数的原型prototype,形成通道,而构造函数上的属性在通道之外

function obj(){ }
obj.prototype.abc = 123;

let ob = new obj();
console.log(ob.abc)//123

delete obj.prototype.abc;//删除原型上的abc属性;
Object.prototype.abc = 456;
console.log(ob.abc)//456

obj.abc = 000;
console.log(ob.abc)//456

判断类型

instanceof——函数在对象的原型链上

语法:二元运算符,用于判断类型 对象 instanceof 函数

定义:判断这个函数的原型是不是在这个对象的原型链上 yes:true no:false

function obj(){
    
}
let aa = new obj();

console.log( aa instanceof obj)//true
console.log( aa instanceof Object)//true

isPrototypeOf()————对象在对象的原型链上

语法:二元运算符,用于判断类型 对象1.isPrototypeOf(对象2)

定义:用于测试对象1是否存在于另对象2的原型链上。 yes:true no:false

function Obj(name){
	this.na = name;
}

let ob = new Obj('nn');
let ob2 = new Obj('mm')

console.log( Obj.prototype.isPrototypeOf(ob2) )//true
console.log( Object.prototype.isPrototypeOf(ob2) )//true

Object.getPrototypeOf(对象)————返回对象的__proto__

调用此方法返回对象的\__proto__,也就是创建这个对象的构造函数的prototype。
function Obj(name){
	this.na = name;
}

let ob = new Obj('nn');

console.log( Object.getPrototypeOf(对象) = Obj.prototype )//true

Object.prototype.toString.call()————终极转为字符

toString会把对应对象的构造函数转为对应的字符串,但是像Array.prototype.toString会把对象转为带逗号的字符串,只有Object.prototype.toString可以将其转为类似[object Array]的字符串
console.log( Object.prototype.toString.call([]) )//[object Array]
console.log( Object.prototype.toString.call({}) )//[object Object]
console.log( Object.prototype.toString.call('') )//[object String]
console.log( Object.prototype.toString.call(123) )//[object Number]
console.log( Object.prototype.toString.call(true) )//[object Boolean]
console.log( Object.prototype.toString.call(null) )//[object Null]
console.log( Object.prototype.toString.call(undefined) )//[object Undefined]

console.log( Object.prototype.toString.call([]).slice(8,-1) )//Array

constructor————拥有这个原型的函数

ele.constructor : 返回创建ele的构造函数

构造函数原型prototype下有一个属性constructor constructor的值指向拥有这个原型的函数

function Con(name){
	this.name = name;
}
let ob = new Con('娜娜');

console.log(ob.constructor === Con.prototype.constructor)//true
console.log(ob.__proto__.constructor === Con.prototype.constructor)//true

原型中的constructor重新赋了一个值后,会被改写,需要手动的把constructor改回来。

function Con(name){
	this.name = name;
}

let ob = new Con('娜娜');

console.log(Con.prototype.constructor)
console.log(ob.constructor)
/*
ƒ Con(name){
	this.name = name;
}
*/

Con.prototype.constructor = 'aaa';//被改写

console.log(Con.prototype.constructor)//aaa
console.log(ob.constructor)//aaa

console.log(ob.constructor === Con)//false;被改写

ob.constructor = Con;
console.log(ob.constructor === Con)//true;将ob的constructor改回来

console.log(Con.prototype.constructor)

hasOwnProperty————判断对象有没有这个属性

定义:判断对象有没有这个属性

语法:对象.hasOwnProperty(属性)

function obj(name){
    this.name = name
}
obj.prototype.abc = 100;

let aa = new obj('hh');

console.log( aa.hasOwnProperty('abc') )//false
console.log( aa.hasOwnProperty('name') )//true

for in

定义:遍历对象所在原型链上的所有的允许被遍历的属性。
function Obj(){
    this.abc = 100;
}
let obj = new Obj();
Obj.prototype.def = 200;

for(var attr in obj){
    console.log(attr);//abc def
}
//遍历出对象和对象构造函数原型上的属性,对象原型链上的所有属性

for(var attr in obj){
	if(obj.hasOwnProperty(attr)){
		console.log(attr)//abc
	}
}

in————是否本身或继承有该属性

in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中

返回值:yes:true;————no :false;

function Obj(name){
	this.na = name;
}

Obj.prototype.abc = '123'

let ob = new Obj('nn');

console.log('na' in ob)//true
console.log('abc' in ob)//true

Object.keys()————枚举属性

取得对象上所有可枚举的实例属性,这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组
function Obj(name,age){
	this.na = name;
	this.nb = age
}

Obj.prototype.abc = '123'

let ob = new Obj('nn');

console.log( Object.keys(ob) )//["na", "nb"]

Object.getOwnPropertyNames()————枚举属性(不论是否可枚举)

得到所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法
function Obj(name,age){
	this.na = name;
	this.nb = age
}
Obj.prototype.abc = '123';

let ob = new Obj('nn',30);

console.log( Object.getOwnPropertyNames(Obj.prototype) )// ["constructor", "abc"]

Object.getOwnPropertyDescriptor()———— 自有属性描述符

定义:方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性) > 返回值:如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。
function Obj(name){
	this.na = name;
}

Obj.prototype.abc = '123'

let ob = new Obj('nn');

console.log(Object.getOwnPropertyDescriptor(ob,'na'))
// {value: "nn", writable: true, enumerable: true, configurable: true}

console.log(Object.getOwnPropertyDescriptor(ob,'abc'))
//undefined
//只会描述自身的属性,不会找原型链上的属性

继承

继承可以使得子类具有父类的属性和方法

包装对象

一切皆对象,只有对象才能有属性,js中的所有都可以看成是对象,String(),Number(),Boolean()是包装对象,通过此方法创建的可以看成是对象,可以有属性,但是通过类似let str = 'abc';这种方法创建的虽然可以添加属性,但是会被立即销毁。
let str = 'abc';
str.a = 'deg';

console.log(str.a)//undefined;
console.log(str.a = 0)//0
//对象属性使用完就销毁了,所以第一次会找不到

console.log(str.valueOf());  // 拿到原始值

let str2 = new String('100')//声明的是对象。
str2.a = 'deg';//对象的属性不会销毁
console.log( typeof str2)//object
console.log(str2.a)//deg//并不是undefined
console.log(str2.valueOf());  //100 拿到原始值