javascript面向对象的详细知识点

198 阅读5分钟

js面向对象知识点

1. 对象的定义

对象 : 是无序属性的集合

2. js的对象分类

  • 内置对象

    • 本地对象
      • Object、Array、String、Number、Boolean、Date、Error.....
    • 单体内置对象
      • 只有 Global 和 Math
    • 两者的区别:
      • 单体对象不需要加new
  • 宿主对象

    • js寄宿环境定义的对象,
      • eg:window、document、history
  • 自定义对象

3. typeof 与 instanceof 的应用

3.1 typeof

typeof

: 输出数据所属的类型

格式

typeof 数据

3.2 instanceof

格式 : 要判断的数据 instanceof 类型

功能 : 查看数据是否是指定的类型

3.3 示例

enter description here

4. 属性的操作

4.1 访问某个属性

  • 方式一:对象名.属性
    • obj.name

  • 方式二:对象名[属性]。
    • obj["name"]

  • 注意:如果属性名被保存在某一个变量,只能使用第二种方式。

4.2 给对象添加属性

  1. 两种方式

    • 对象名.属性名 = 属性值;

      • obj.mame = "zhangsan"

    • 对象名[“属性名”] = 属性值;

      • obg["name"] = "zhangsan"

  2. 属性有四个特征

    • configurable:是否可以删除。默认为true。
    • writable:是否可以修改属性的值。默认为true。
    • enumerable:是否可以修改属性的值。默认为true。
    • value:值。默认为undefined
  3. 精细设置对象的属性

格式

Object.defineProperty(对象名,“属性名”,{
	Configurable:
	Writable:
	Enumerable:
	Value:
})

示例: enter description here

  1. 案例:设置一个只读属性,模拟常量

enter description here

4.3 获取全部可以枚举的实例属性

Object.keys();

enter description here

5. 利用属性的特性,完成例子

5.1 数组去重

let arr = [1,1,2,2,3,3];
let newArr = [];
let obj = {};
for(i=0;i<arr.length;i++){
    let t = arr[i];
    if(obj[t]){

    }else{
        obj[t] = true;
        newArr.push(t);
    }   
}
console.log('newArr :', newArr);

输出结果:

enter description here

5.2 统计数组中个元素出现次数

let arr = [1,1,2,2,3,3];
let obj = {};
for(i=0;i<arr.length;i++){
    let t = arr[i];
    if(obj.hasOwnProperty(t)){
        obj[t] += 1;
    }else{
        obj[t] = 1;
    }   
}
console.log('obj :', obj);

输出结果:

enter description here

6. 原型与原型链

6.1 原型

prototype : 就是原型

  1. 每一个函数都有一个prototype属性
  2. 函数的原型是一个对象,原型里面与很多方法,但是一定有constructor方法
  3. constructor指向这个函数本身

enter description here

proto : 隐式原型,它对外是隐藏的,我们在程序开发过程,不会直接使用它。

  1. 每一个对象都有一个__proto__属性
  2. 对于函数而言,每一个函数都会有一个prototype和一个__proto__属性。

结论:

对象的__proto__属性 指向 创建这个对象的函数的prototype

6.2 原型链

原型链 : 访问一个对象的属性时,先在这个对象自己的属性中去找,如果找不到,则沿着__proto__这个属性向中找,如果__proto__这个对象中还是没有找到,就在__proto__对象的__proto__属性中去找,依次下去,这就是原型链。

enter description here

注意

  • 由于对象的__proto__[隐式原型]与创建这个对象的函数(构造器)的prototype是一致的
  • 所以理论上,你对__proto__修改会直接影响prototype。
  • 建议只使用prototype[原型]

7. this 的应用

7.1 如何确定this的值

答:谁调用了this就是谁。

看方法前的那个对象是谁,this就指向谁。

  • (1)有明确的对象

    • 例如:Obj.say();say 的当前对象是obj,所以say中的this 就指向obj。
  • (2)没有明确的对象

    • 如果这个函数没有明确说是哪个对象的,则它肯定是属于window对象的,所以this就会指向window.
  • (3)如果是call和apply的方式,这时,this指向第一个参数

    • 例如:f.call(obj1),f中的this指向obj1。

7.2 this 的总结

  1. this出现在全局中,thiswindow
  2. this出现在一个普通的函数中(不是方法),this是window
  3. this出现在一个对象的方法,this可以是这个对象,也可以是window
  4. call和apply可以改变this指向,指向()第一个参数
  5. 一个函数没有明确指出谁调了,this是window

7.3 call,apply与bind可以改变this的指向

请参考

8. 创建对象的N种方式

8.1 字面量方式

例子:矩形对象

enter description here

  • 优点:直接了当,yimuliao
  • 缺点:不能批量生产对象,只能一个一个写

8.2 工厂模式

例子:

// 利用工厂模式创建对象   工厂利用一个函数来模拟
function factory(w,h){
    // 批量地产生矩形对象
    let obj = {}
    obj.width = w;
    obj.height = h;
    obj.getC = function(){
        return (this.width+this.height)*2
    }
    obj.getS = function(){
        return this.width*this.height
    }
    return obj;
}
let r1 = factory(1,2)
let r2 = factory(2,3)
console.log(r1.getC()) // 6
console.log(r1.getS())  // 2
console.log(r2.getC()) // 10
console.log(r2.getS()) // 6
  • 优点:主要解决了字面量方式的不能批量生产对象的问题。

  • 缺点:他产生的对象没有“商标”(不知道是谁生产的)

8.3 构造器模式(使用new)

示例:

new做了如下四件事:

  1. 创建一个对象 var o = {};

  2. F.call(o); // 先执行F()函数,同时把this用o来代替。把F()构造器设置的各种属性值,直接赋给o. 理解如下代码: enter description here

  3. o.proto = F.prototype //让o具备F.prototype上定义的方法。

  4. return o;


  • 构造器模式优点:

    • 解决了对象的来源不明的问题。我们可以通过对象的constructor属性,找到它的构造器。
    • new 构造器() 这种格式会受面向对象的程序员(java,C#,C++…)所接受。
    • 具备工厂模式的批量生产(还可以是定制的,例如传参不同) 。
  • 构造器模式缺点:不能像数组一组,共用方法。内存浪费。

8.4 构造器 + 原型 模式

function Rect(w, h) {
        this.width = w;
        this.height = h;
    }
    Rect.prototype.getC = function(){
        return (this.width+this.height)*2
    }
    Rect.prototype.getS = function(){
        return this.width*this.height
    }
    // r1  r2  r3都有getC和getS  造成内存空间的浪费
    let r1 = new Rect(1, 2);
    let r2 = new Rect(2, 3);
    console.log(r1.getC()) // 6
    console.log(r1.getS())  // 2
    console.log(r2.getC()) // 10
    console.log(r2.getS()) // 6

9. 继承

9.1 原型继承

function Parent(yourname) {
	this.name = yourname;
}
Parent.prototype.say = function () {
	console.log(this.name)
}

// 让Son去继承name属性和say方法
function Son(yourname) {
	// 继承属性
	Parent.call(this,yourname)
}

// 继承方法
Son.prototype = Parent.prototype;
Son.prototype.constructor = Son;

//调用
let s = new Son("hrllo");
s.say();

9.2 继承的浅拷贝

浅拷贝 : 将父类的原型对象直接复制给子类的原型对象

Son.prototype = Parent.prototype;
Son.prototype.constructor = Son;

若有多个对象,多个对象指向同一个原型对象,parent,肯能存在干扰

9.3 继承的深拷贝

深拷贝 : 把原型对象copy一份 让Son继承copy过来的

for(let i in Parent.prototype){
        Son.prototype[i] = Parent.prototype[i]
    }

每个对象各自独立,不存在干扰的现象

10 class的应用

10.1 使用css创建对象

格式

class 类名{
	constructor(参数){
		this.属性= 参数;
	}
	method(){//对象中简写方法,省略了function。不要与箭头函数搞混了。
	}

}

注意

  • class是关键字,后面紧跟类名,类名首字母大写,采取的是大驼峰命名法则。类名之后是{}。
  • 在{}中,不能直接写语句,只能写方法,方法不需要使用关键字
  • 方法和方法之间没有逗号。不是键值对。 例子

调用方式: enter description here

10.2 使用extends实现继承

格式

class 子类 extends 父类{
	constructor(参数){
  		super(参数)
		this.属性 = 值
	}
}

注意

  • 使用extends关键字来实现继承
  • 在子类中的构造器constructor中,必须要显式调用父类的super方法,如果不调用,则this不可用

示例:

class NBAPlayer {
	constructor(name, age, height) {
		this.name = name
		this.age = age
		this.height = height
	}
	say() {
		console.log(`我是${this.name},今年${this.age},我的身高是${this.height}`)
	}
	jump() {
		console.log("jump...")
	}
}
class MVP extends NBAPlayer {
	constructor(name, age, height, year) {
		super(name, age, height)
		this.year = year;
	}
	showMVP() {
		console.log(`我是${this.name},我是${this.year}年的MVP`)
	}
}
var m1 = new MVP("xiaoqiang","33","191",2010)
m1.say()
m1.showMVP()