一、js中的this
1、this的概念
注意:this没有继承
function f(){
console.log(this.x)
};
var obj = {
f:f,
x:1
};
var x = 2;
f();
obj.f();
若不使用箭头函数,使用临时变量保存一下this
let foo = function(){
var self = this;//this 就是obj对象
console.log(this===obj);
f();
function f(){
console.log(this===obj);
console.log(self === obj);
}
}
var obj = {
m:foo
}
obj.m()
2、this的指向
- 全局作用域的this window||global
function sayHello(){
console.log(this)
}
sayHello()//输出window(非严格模式)或者undefined(严格模式)
- 函数调用中的this
- 对象方法中的this
const ob = {
name:'aa',
sayName:function(){
console.log(this.name)
}
}
obj.sayName()
- 构造函数中的this
function Person(name){
this.name = name
}
const alice = new Person('aa');
console.log(alice.name)
- 箭头函数中的this(不会创建新的this上下文,而是捕获外层作用域中的this)
const obj = {
name:'Alice',
sayName:()=>{
console.log(this.name)
}
}
obj.sayName()
- 事件处理程序中this,this通常指向触发元素对象
document.getElementById('myButton').addEventListener('click',function(){
console.log(this)//输出<button id="muButton"></button>
})
3、如何改变this(call\apply\bind)
这些都可以改变函数执行时this的值
function greet(greeting){
console.log(greeting+','+this.name)
}
const person = {name:'Alice'}
greet.call(person,'Hello')
greet.apply(person,['Hello'])
const boundGreet = greet.bind(person)
boundGreet('Hello')
二、面向对象
1、什么是对象
2、什么是面向对象
- 面向过程
- 面向对象(OOP)
3、面向对象的特点
- 继承
- 封装
- 多态
//处理学生的成绩表,通过面向过程的方式
var stu1 = {name:'aa',score:98};
var stu2 = {name:'bb',score:88};
//处理学生成绩可以通过函数去实现,比如打印
function printScore(student){
console.log(student)
}
//假如使用面向对象
function Student(name,score){
this.name = name
this.score = score
this.printScore = function(){}
}
//根据模版创建具体实例对象
var std1 = new Student('aa',98)
var std2 = new Student('bb',88)
//打印成绩
std1.printScore()
std2.printScore()
三、基于函数的面向对象实现
1、创建对象
- new Object()
- 对象字面量
2、工厂函数=〉构造函数
如果有10个学生信息,需要var10次对象,所以引入工厂函数
function createPerson(name,age){
return {
name,
age,
sayName:function(){
console.log(this.name)
}
}
}
var person1 = createPerson('aa',12)
var person2 = createPerson('bb',13)
问题:不能识别对象是什么类型。是否属于Person。
3、构造函数的问题
用以解决上述person1\person2无法识别引用的是哪个对象的问题
function Person(name,age){
this.name = name;
this.age = age
this.sayName = function(){
console.log(name)
}
}
var person1 = new Person('aa',18)
var person2 = new Person('bb',19)
4、new做了哪些事情
- 创建一个新对象
- 将构造函数的作用域赋给新对象(this指向新对象)
- 执行构造函数中的代码
- 返回新对象
function Person(name,age){
//使用new 调用Person()时,创建一个对象
//var instance = {}
//让内部的this指向instance对象
//this=instance
//接下来所有针对this的操作就是instance
//return this
}
console.log(p1.constructor === Person)//true
console.log(p2.constructor === Person)//true
console.log(p1.constructor === p2.constructor)//true
console.log(p1 instanceof Person)//true 推荐使用方式
问题:存在浪费内存(每生成一个实例都会有一段重复的代码,如sayName方法。如果有几十万个学生去初始化这个Person,会造成极大的内存浪费)
function Person(name,age){
this.name = name;
this.age = age
this.type = 'human'
this.sayName = function(){
console.log(name)
}
}
var person1 = new Person('aa',18)
var person2 = new Person('bb',19)
console.log(person1.sayName === person2.sayName)//false
解决方案:
function sayName(){
console.log(this.name)
}
function Person(name,age){
this.name = name;
this.age = age
this.type = 'human'
this.sayName = sayName
}
var person1 = new Person('aa',18)
var person2 = new Person('bb',19)
console.log(person1.sayName === person2.sayName)//true
问题:sayName成了全局变量,不够优雅。解决:可放原型上
5、原型和原型链
function Person(name,age){
this.name = name;
this.age = age
}
Person.prototype.type='human'
Person.prototype.sayName = function(){
console.log(this.name)
}
var person1 = new Person('aa',18)
var person2 = new Person('bb',19)
console.log(person1.sayName === person2.sayName)//true
function F(){
sayHi() {}
}
console.log(F.prototype)//Object
console.log(F.prototype.constructor===F)//true
var instance = new F()
console.log(instance.__proto__ === F.prototype)//true
四、es6中的面向对象
1、Classes(类) 和 Object(对象)
- 类是一种特定对象中方法和变量的模版定义
- 对象是类的具体实例,并且在内存中分配
注意(大部分浏览器原生不支持这种class,一般通过打包工具编译成function之后才能运行)
//定义类
class Form{
submit(){
console.log(this.name+''+this.role+'表单已提交')
}
cancel(){
console.log(this.name+''+this.role+'表单已取消')
}
fill(give_name,role){
this.name=give_name
this.role = role
}
}
//创建对象
const student1Form = new Form()
student1Form.fill('aa',14)
const student2Form = new Form()
student2Form.fill('bb',14)
student2Form.cancel()
student1Form.submit()
student2Form.submit()
2、Constructor(构造函数)
//定义类
class Form{
constructor(){
//构造函数初始化默认的name和role
this.name = 'test';
this.role = 'Student'
}
submit(){
console.log(this.name+''+this.role+'表单已提交')
}
cancel(){
console.log(this.name+''+this.role+'表单已取消')
}
fill(give_name,role){
this.name=give_name
this.role = role
}
}
//创建对象
const student1Form = new Form()
//student1Form.fill('aa',14)
const student2Form = new Form()
//student2Form.fill('bb',14)
student2Form.cancel()
student1Form.submit()
student2Form.submit()
无参构造函数:没有参数的构造函数
class Example{
constructor(){
this.name = 'test'
}
}
有参构造函数:一个带有参数的构造函数
class Example{
constructor(value){
this.name = value
}
}
3、Inheritance(继承)
一个类从另一个类中继承的属性和能力
class Animal {
constructor(name,color,age){
this.name = name
this.color = color
this.age = age
}
run(){
console.log(this.name+'正在跑')
}
shout(){
console.log(this.name+'正在叫')
}
sleep(){
console.log(this.name+'在睡觉')
}
}
class Monkey {
constructor(name,color,age){
this.name = name
this.color = color
}
run(){
console.log(this.name+'正在跑')
}
shout(){
console.log(this.name+'正在叫')
}
sleep(){
console.log(this.name+'在睡觉')
}
eatBanana(){
console.log(this.name+'在吃香蕉')
}
}
const animal1 = new Monkey('猴子','棕色',2)
const animal2 = new Animal('狗','白色',3)
animal1.eatBanana()
animal2.shout()
通过继承改写
//父类-基类
class Animal {
constructor(name,color,age){
this.name = name
this.color = color
this.age = age
}
run(){
console.log(this.name+'正在跑')
}
shout(){
console.log(this.name+'正在叫')
}
sleep(){
console.log(this.name+'在睡觉')
}
}
//子类-派生类
class Monkey extends Animal{
eatBanana(){
console.log(this.name+'在吃香蕉')
}
hide(){
console.log(this.name+'在躲藏')
}
}
const animal1 = new Monkey('猴子','棕色',2)
const animal2 = new Animal('狗','白色',3)
animal1.eatBanana()
animal2.shout()
animal1.hide()
上面是单继承,还有多继承,一个类继承另一个类,这个类又继承另一个父类
//父类-基类
class Animal {
}
class Mammal extends Animal{}
class Dog extends Mammal{}
层次继承:多个类继承同一个类
//父类-基类
class Animal {
}
class Mammal extends Animal{}
class Dog extends Animal{}
- 多重继承:一个子类同时继承多个父类的方法(js不直接支持多重继承,但可以通过mixin实现类似的效果)
//Mixin1 可飞行
const fly = (Base)=>class extends Base{
fly(){}
}
//Mixin2 可以跑
const run = (Base)=>class extends Base{
run(){}
}
//基类
class Animal{
constructor(name){
this.name = name
}
move(){}
}
//可以通过Mixin实现多重继承
class Duck extends fly(run(Animal)){}
- 混合继承:通过多种继承方式,mixin去实现
//Mixin1 可飞行
const fly = (Base)=>class extends Base{
fly(){}
}
//基类
class Animal{
constructor(name){
this.name = name
}
move(){}
}
class FlyAnimal extends fly(Animal){};
class Duck extends FlyAnimal{}
4、重写
class Human {
constructor(name, age, bodyType) {
this.name = name;
this.age = age;
this.bodyType = bodyType;
}
getName(){}
getAge(){}
getBodyType(){}
}
- super:用于调用父类的构造函数,以访问其属性和方法
- 重写构造函数
class Human {
constructor(name, age, bodyType) {
this.name = name;
this.age = age;
this.bodyType = bodyType;
}
getName(){}
getAge(){}
getBodyType(){}
}
class Student extends Human{
constructor(){
super('aa',80,'胖')
}
}
const std1 = new Student()
std1.getName()
重写方法
class Student extends Human{
constructor(){
super('aa',80,'胖')
}
//使用super关键字在子类中重写方法 方法名同名,同参,且仅发生在类继承关系中
getAge(){
super.getAge()
}
}
5、重载
class Calculator {
add(a,b){
return a+b
}
add(a,b,c){
return a+b+c
}
}
const calc = new Calculator()
console.log(calc.add(1,2))//NaN
可以hack
class Calculator {
add(...args){
if(args.length>0)return args.reduce((sum,num)=>sum+num,0)
}
}
// 柯里化 也能解决上述问题
// 经典面试题
// f(1)(2)(3) =>6
// f(1)(2) =>3
// ...
6、访问修饰符
- public: 可以从其他任何类中访问
- protected:受保护的成员,可以在同一类及其子类中访问
- private:私有成员,只能在同一个类中访问
7、静态方法
static:给类定义一个静态方法或字段,静态方法是属于类本身的方法,而不是类的具体实例的方法。也可被继承。
class Animal{
constructor(){}
static cap(name){console.log(name)}
static init(){}
}
class Calc{
add(){
return Animal.cap('bb')//在非静态方法中使用静态方法
}
}
Animal.cap('aa')
Animal.init()
8、Getter和Setter
class Animal{
constructor(){}
get getName(){
return this.name
}
set setName(name){
this.name = name
}
}
const dog = new Animal()
dog.setName = 'xx';
dog.getName()
9、instanceOf
检查一个对象是否是某个类或者接口的实例
10、封装
是一种限制对对象某些组件或属性直接访问的方式
class Bank {
#balance;// 私有字段
}
11、多态
多种继承形式,一种函数的不同表达式
12、抽象
内部方法不展示实现方法,是隐藏的。一种设计思路,用于工具库和组件库的实现。