js面对对象
Object Oriented Programming,OOP
是一种编程范式,它将代码分为具有属性和方法的对象。
1.编程范式
指的是计算机编程的风格或典范模式。
根据编程范式来分类大致可以分为2大类:命令式编程和声明式编程
1.命令式编程
以命令为主的,给机器提供一条又一条的命令序列让其原封不动的执行。程序执行的效率取决于执行命令的数量。
常见命令式编程:C语言,C++,Java,C#
命令式编程又可以分为2大类:面向过程和面向对象
面向过程:就是分析出解决问题所需要的步骤,然后把这些步骤一步一步实现
面向对象:面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。
2.声明式编程
告诉“机器”你想要的是什么,让机器想出如何去做。
分为两大类:领域专用语言和函数式编程
2.面向对象的特征
面向对象具备3大特征,分别是封装,继承,多态。
3.描述对象
#####4.类
对象就是从类里面产生出来的
类是对象的一种概括,而对象是类的一种具体实现
2.原型对象
1.原型对象
每一个对象,都有一个原型对象。而原型对象上面也有一个自己的原型对象,一程一程向上找,最终会到达null
2.对象分类
分为两大类原生对象和宿主对象
原生对象又可以分为2类:内置对象和定义对象
3.宿主对象
就是依存于某一个特定的环境才会有的对象。
3.基于对象创建新对象
ECMAScript 5 中提供了Object.create() 方法,可以用来克隆对象
咧:
const person = {
arms: 2,
legs: 2,
walk() {
console.log('walking');
}
}
const xiejie = Object.create(person);
console.log(xiejie.arms); // 2
console.log(xiejie.legs); // 2
xiejie.walk(); // walking
console.log(xiejie.__proto__ === person); // true
使用Object.create()方法克隆对象的时候,还可以传入第2个参数,第2个参数是一个JSON对象,里面可以书写新对象的一些属性设定:
const person = {
arms: 2,
legs: 2,
walk() {
console.log('walking');
}
}
const xiejie = Object.create(person, {
name: {
value: "xiejie",
writable: false,
enumerable: true
},
age: {
value: 18,
enumerable: false
}
});
console.log(xiejie.name); // xiejie
console.log(xiejie.age); // 18
console.log(xiejie.arms); // 2
console.log(xiejie.legs); // 2
for (let i in xiejie) {
console.log(i); // name arms legs walk
}
4.原型相关方法
1. prototype 和__proto__
prototype是构造函数上面的一个属性,指向一个对象,这个对象是该构造函数实例化出来的对象的原型对象
const arr = [1,2,3,4,5];
console.log(Array.prototype); // []
console.log(arr.__proto__); // []
console.log(Array.prototype === arr.__proto__); // true
2. Object.getPrototypeOf()方法
let arr = [1,2,3,4,5]
console.log(Object.getPrototypeOf(arr)); // []
console.log(arr.__proto__); // []
console.log(Object.getPrototypeOf(arr) === arr.__proto__); // true
3. constructor 属性
通过constructor属性,我们可以查看到一个对象的构造函数是什么
const arr = [1,2,3,4,5];
console.log(arr.constructor); // [Function: Array]
4. instanceof 操作符
判断一个对象是否是一个构造函数的实例,如果是返回true,否则就返回false
const arr = [1,2,3,4,5];
console.log(arr instanceof Array); // true
console.log(arr instanceof Number); // false
5. isPrototypeOf()方法
该方法将会返回一个布尔值,主要用于检测一个对象是否是一个实例对象的原型对象
const arr = [1,2,3,4,5];
console.log(Array.prototype.isPrototypeOf(arr)); // true console.log(arr.__proto__.isPrototypeOf(arr)); // true
6. hasOwnProperty()方法
判断一个属性是定义在对象本身上面还是从原型对象上面继承而来的。如果是本身的,则返回true,如果是继承而来的,则返回false
const person = {
arms: 2,
legs: 2,
}
const xiejie = Object.create(person, { name: {
value: "xiejie",
writable: false,
enumerable: true
},
age: {
value: 18,
enumerable: false
} });
console.log(xiejie.hasOwnProperty("name")); // true console.log(xiejie.hasOwnProperty("age")); // true console.log(xiejie.hasOwnProperty("arms")); // false console.log(xiejie.hasOwnProperty("legs")); // fals
5.构造函数
1.构造函数创建对象
使用函数来模拟其他面向对象语言中的类。
用于实列化对象的函数,我们将其称之为构造函数。
咧:
const Computer = function(name,price){
this.name = name;
this.price = price;
}
Computer.prototype.showSth = function(){
console.log(`这是一台{this.name}电脑`);
}
当使用new运算符调用函数时,该函数总会返回一个对象,通常情况下,构造函数里面的this就指向返回这个对象。
咧:
const Computer = function(name,price){
this.name = name; this.price = price;
}
Computer.prototype.showSth = function(){
console.log(this); // 打印出this所指向的对象
console.log(`这是一台{this.name}电脑`);
}
const apple = new Computer("苹果",12000);
console.log(apple.name);// 苹果
console.log(apple.price);// 12000
apple.showSth();// Computer { name: '苹果 ', price: 12000 } 这是一台苹果电脑
const asus = new Computer("华硕",5000);
console.log(asus.name);// 华硕
console.log(asus.price);// 5000
asus.showSth();// Computer { name: ' 华硕', price: 5000 } 这是一台华硕电脑
2.构造函数显式返回内容
构造函数显式返回object类型对象的区别
咧:
const Computer = function (name, price) {
this.name = name;
this.price = price;
}
Computer.prototype.showSth = function () {
console.log(this); // 打印出this所指向的对象
}
const apple = new Computer("苹果", 12000);
console.log(apple.name); // 苹果
apple.showSth(); // Computer { name: ' 苹果 ', price: 12000 }
3.ECMAScript 6中类的声明
使用class关键字来声明一个类来,然后从类里面实列化对象
咧:
class Computer {
// 构造器
constructor(name, price) {
this.name = name;
this.price = price; }
//原型方法
showSth() {
console.log(`这是一台${this.name}电脑`);
}
} const apple = new Computer("苹果", 12000);
console.log(apple.name); // 苹果
console.log(apple.price); // 12000
apple.showSth(); // 这是一台苹果电脑
4.静态方法
又被称为类方法,就是通过类来调用的方法。静态方法的好处在于不需要实例化对象,直接通过类就能够进行方法的调用。
咧:
class Computer {
// 构造器
constructor(name, price) {
this.name = name;
this.price = price; }
//原型方法
showSth() {
console.log(`这是一台${this.name}电脑`);
}
// 静态方法
static comStruct() {
console.log("电脑由显示器,主机,键鼠组成");
}
}
Computer.comStruct(); //电脑由显示器,主机,键鼠组成
如果书写的式构造函数,也有办法来模拟静态方法,直接将方法挂在构造函数上。
咧:
const Computer = function (name, price) {
this.name = name;
this.price = price;
}
Computer.prototype.showSth = function () {
console.log(`这是一台${this.name}电脑`);
}
//静态方法直接通过 Computer这个构造函数来调用
Computer.comStruct = function () {
console.log("电脑由显示器,主机,键鼠组成");
}
Computer.comStruct(); //电脑由显示器,主机,键鼠组成
面对对象3大特征
1.封装
指隐藏内部的细节,不暴露在外面。目的就是将信息隐藏。
const Computer = function(name,price){
this.name = name;
this.price = price;
this.sayPrice = function () {
console.log(`价格${_price}`);
}
}
Computer.prototype.showSth = function () {
console.log(`这是一台${this.name}电脑`);
}
const apple = new Computer("苹果", 12000);
apple.sayPrice(); // 价格12000
2.继承
1.继承基本介绍
面向对象里面的继承是指一个子类去继承一个父类。子类继承父类之后,父类所有的属性和方法都自动拥有了
好处:代码复用。
缺点:1)会出现“大猩猩与香蕉”问题
2)也会遇到“菱形继承”(只会在多继承里面才会出现)
2.对象冒充
对象冒充,就是用父类(构造函数)去充当子类的属性
咧:
const Person = function (name, age) {
this.name = name;
this.age = age;
}
const Student = function (name, age, gender, score) {
this.temp = Person; // 将Person构造函数Student的一个属性
this.temp(name, age); // 给Person构造函数里面的this.name以及this.age赋值
delete this.temp; // this.temp 已经无用,将其删除
this.gender = gender;
this.score = score;
}
const xiejie = new Student("哈哈", 18, "男", 100); console.log(xiejie.name); // 哈哈
console.log(xiejie.age); // 18
console.log(xiejie.gender); //男
console.log(xiejie.score); // 100
使用对象冒充来实现继承也有一个缺陷,那就是无法继承到原型对象上面的属性。
咧:
const Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.test = "this is a test";
const Student = function (name, age, gender, score) {
this.temp = Person; // 将Person构造函数Student的一个属性
this.temp(name, age); // 给Person构造函数里面的this.name以及this.age赋值
delete this.temp; // this.temp 已经无用,将其删除
this.gender = gender;
this.score = score;
}
const xiejie = new Student("哈哈", 18, "男", 100); console.log(xiejie.name); // 哈哈
console.log(xiejie.age); // 18
console.log(xiejie.gender); //男
console.log(xiejie.score); // 100
console.log(xiejie.test); // undefined
3.方法借用模式
call()和apply()方法
Function.apply(obj҅args)方法能接收两个函数
obj:这个对象将代替Function类里this对象
args:这个是数组,它将作为参数传给Function(args->arguments)
Function.call(obj҅[param1[҅param2[҅…[҅paramN]]]])
obj:这个对象将代替Function类里this对象
params:这个是一个参数列表
这两个方法本质上作用是一样的,就是一个方法,区别主要就是参数的接收方式不同
这两个方法是在Function.prototype.call、Function.prototype.apply
A.call(B,参数) A代表一个方法,B代表this指向,将A这个方法应用到B上面(相当于B借用A方法)
call()实现继承示列:
const Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.test = "this is a test";
const Student = function (name, age, gender, score) {
// 将Person构造函数应用到this 里面
// this 后面是参数
Person.call(this, name, age);
this.gender = gender;
this.score = score;
}
const xiejie = new Student("哈哈", 18, "男", 100); console.log(xiejie.name); // 哈哈
console.log(xiejie.age); // 18
console.log(xiejie.gender); // 男
console.log(xiejie.score); // 100
console.log(xiejie.test); // undefined
apply()实现继承示列:
const Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.test = "this is a test";
const Student = function (name, age, gender, score) {
// 将Person构造函数应用到this 里面
// this 后面是参数
## Person.apply(this,[name,age]);
this.gender = gender;
this.score = score;
}
const xiejie = new Student("哈哈", 18, "男", 100); console.log(xiejie.name); // 哈哈
console.log(xiejie.age); // 18
console.log(xiejie.gender); // 男
console.log(xiejie.score); // 100
console.log(xiejie.test); // undefined
4.原型继承
这种方式的核心思路就是改变构造函数的prototype的指向,使其指定到我们想要继承的类的实列对象上面
咧:
const Person = function (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.test = "this is a test";
const Student = function (name, age, gender, score) {
// 将Person构造函数应用到this里面
// this后面是参数
Person.apply(this, [name, age]);
this.gender = gender;
this.score = score;
}
Student.prototype = new Person(); // 改变Student构造函数的原型对象
const xiejie = new Student("哈哈", 18, "男", 100); console.log(xiejie.name); //哈哈
console.log(xiejie.age); // 18
console.log(xiejie.gender); // 男
console.log(xiejie.score); // 100
console.log(xiejie.test); // this is a test
5.ECMAScript 6 继承方式
可以使用extends关键字来实现继承
咧:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
console.log(`my name is ${this.name}`);
}
}
class Student extends Person {
constructor(name, age, gender, score) {
super(name, age); // super代表访问父类的构造函数
this.gender = gender;
this.score = score;
}
learn() {
console.log("I\'m learning");
}
}
const xiejie = new Student("xiejie", 18, "male", 100); console.log(xiejie.name); // xiejie
console.log(xiejie.age); // 18
console.log(xiejie.gender); // male
console.log(xiejie.score); // 100
xiejie.sayName(); // my name is xiejie
xiejie.learn(); // I'm learning
3多态
1.多态简介
含义:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。
换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。
2.实现多态
const duck = {
name: "鸭子",
makeSound: function () {
console.log("嘎嘎嘎");
}
};
const chicken = {
name: "鸡",
makeSound: function () {
console.log("咯咯咯");
}
};
const animalSound = function (obj) {
obj.makeSound();
}
animalSound(duck); // 嘎嘎嘎
animalSound(chicken); // 咯咯咯
在 ECMAScript 6 中我们也可以在子类里面书写和父类相同的方法名来进行覆盖
咧:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
console.log(`my name is ${this.name}`);
}
}
class Student extends Person {
constructor(name, age, gender, score) {
super(name, age); // super代表访问父类的构造函数
this.gender = gender;
this.score = score;
}
sayName() {
console.log(`haha,I'm ${this.name}`);
}
learn() {
console.log("I\'m learning");
}
}
const xiejie = new Student("哈哈", 18, "男", 100);
xiejie.sayName(); // haha,I'm 哈哈
const song = new Person("宋", 20);
song.sayName(); // my name is 宋
3.多态的意义
多态的最根本的好处在于,你不必再向对象询问”你是什么类型“后根据得到的答案调用对象的某个行为,你只需要调用该行为就是了,其他的一切多态机制会为你安排妥当。
7.this的指向
1.this指向的默认情况
1.this指向当前对象
如果是在对象里面使用this,则指向当前对象。this可以用在方法内,获取对对象属性的访问。。
咧:
const person = {
name: 'xiejie',
age: 18,
intro: function () {
console.log(this); // { name: 'xiejie', age: 18, intro: [Function: i ntro] }
console.log(`My name is ${this.name},I'm ${this.age} years old`); // My name is xiejie,I'm 18 years old
}
}
person.intro();
2.普通函数中的this指向
如果是普通调用函数,this则指向全局对象
咧:
const test = function () {
console.log(this); // global 对象
// 在 node里面 , 全局对象就是global 对象
// 如果是在浏览器里面,那么全局对象代表window对象
}
test();
3.this指向绑定事件的元素
DOM元素绑定事件时,事件处理函数里面的this指向绑定了事件的元素。这个地方一定要注意它和target的区别,target是指向触发事件的元素。
2.改变this的指向
1.方法借用函数修改this指向
call()和apply()也算是间接修改了this的指向。
const Test = function () {
this.name = "JavaScript";
this.say = function () {
console.log(`这是${this.name}`);
}
}
const test = new Test();
test.say(); // 这是JavaScript
const a = {
name: "PHP"
};
test.say.call(a); // 这是 PHP
2.bind方法绑定this指向
可以通过bind()方法来绑定this的指向。
咧:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数:
thisArg:当绑定函数被调用时,该函数会作为原函数运行时的this指向。
arg1,arg2,…:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
咧:
const a = {
name: "PHP"
};
const Test = function () {
this.name = "JavaScript";
this.say = function () {
console.log(`这是${this.name}`);
}.bind(a); }
const test = new Test();
test.say(); // 这是 PHP
3.箭头函数的this指向
箭头函数的this指向始终为外层的作用域。
注:箭头函数不能作为构造函数。