JavaScript面向对象三大特性

bind/call/apply三个方法

这三个方法都是用于修改函数或者方法中的this

bind方法作用
  • 修改函数或者方法中的this为指定的对象,并且会返回一个修改之后的新函数
  • bind方法也可以传递参数, 参数必须写在this对象的后面
let obj = {
    name:'Jason Wang'
}

function test1() {
    console.log(this);
}
// 将test1中的this指定为obj对象,并返回一个新的函数fn1
let fn1 = test1.bind(obj);  // {name: "Jason Wang"}
fn1()

function test2(a,b) {
    console.log(a, b, this);      
}
// 将test2中的this指定为obj对象,并接受两个参数传递给test2,返回一个新的函数fn2
let fn2 = test2.bind(obj,10,20);  // 10 20 {name: "Jason Wang"}
fn2();
call方法作用
  • 修改函数或者方法中的this为指定的对象, 并且会立即调用修改之后的函数
  • call方法也可以传递参数, 参数必须写在this对象的后面
let obj = {
    name:'Jason Wang'
}

function test1() {
    console.log(this);
}
test1.call(obj);   // {name: "Jason Wang"}

function test2(a,b) {
    console.log(a, b, this);   
}
test2.call(obj,10,20);  // 10 20 {name: "Jason Wang"}
apply方法作用
  • 修改函数或者方法中的this为指定的对象, 并且会立即调用修改之后的函数
  • apply也可以传递参数, 只不过参数必须通过数组的方式传递
let obj = {
    name:'Jason Wang'
}

function test1() {
    console.log(this);
}
test1.apply(obj);   // {name: "Jason Wang"}

function test2(a,b) {
    console.log(a, b, this);
}
test2.apply(obj,[10,20]);   // 10 20 {name: "Jason Wang"}

强类型语言与弱类型语言

强类型语言
  • 一般编译型语言都是强类型语言,
  • 强类型语言,要求变量的使用要严格符合定义
  • 例如定义 int num; 那么num中将来就只能够存储整型数据
弱类型语言
  • 一般解释型语言都是弱类型语言,
  • 弱类型语言, 不会要求变量的使用要严格符合定义
  • 例如定义 let num;,num中既可以存储整型, 也可以存储布尔类型等

面向对象三大特性

封装性

封装性就是隐藏实现细节,仅对外公开接口

为什么要封装
  • 不封装的缺点:当一个类把自己的成员变量暴露给外部的时候,那么该类就失去对属性的管理权,别人可以任意的修改你的属性
  • 封装是将数据隐藏起来,只能用此类的方法才可以读取或者设置数据,不可被外部任意修改
  • 封装是面向对象设计本质(将变化隔离),这样降低了数据被误用的可能 (提高安全性和灵活性)
对象的私有变量和函数
  • 默认情况下对象中的属性和方法都是公有的, 只要拿到对象就能操作对象的属性和方法
  • 外界不能直接访问的变量和函数就是私有变量和是有函数
  • 构造函数的本质也是一个函数,所以会开启一个新的作用域,所以在构造函数中定义的变量和函数就是私有属性和方法
  • 由于私有属性的本质就是一个局部变量,并不是真正意义上的属性,所以如果通过 对象.xxx的方式是找不到私有属性的,会给当前对象新增一个xxx属性
function Person(myName,myAge) {
    // 公有属性
    this.name = myName;
    this.age = myAge;

    // 私有属性
    let position = 'Beijing';

    // 公有接口
    this.setPosition = function (pst) {
        position = pst;
    };

    // 公有接口
    this.getPosition = function () {
        return position;
    };

    // 公有方法
    this.say = function () {
        console.log("hello world");
    }
}
let obj = new Person('Jason',24);

// 通过接口获取 position
console.log(obj.getPosition());   // Beijing

// 通过接口设置 position,操作的是私有属性
obj.setPosition('Shanghai');
console.log(obj.getPosition());    // Shanghai

// 会给obj对象新增一个position属性,操作的是公有属性
obj.position = 'Guangzhou';
console.log(obj.position);    // Guangzhou'
属性和方法分类
  • 实例属性与实例方法
    • 通过构造函数创建的对象,称为实例对象
    • 通过实例对象访问的属性,称为实例属性
    • 通过实例对象调用的方法,称为实例方法
  • 静态属性与静态方法
    • 构造函数也是一个对象,所以也可以给构造函数动态添加属性和方法
    • 通过构造函数访问的属性,称为静态属性
    • 通过构造函数调用的方法,称为静态方法
function Person() {
    this.name = "Jason";
    this.say = function () {
        console.log("hello world"
    }
}
// 创建实例对象
let obj = new Person();
// 访问实例属性
console.log(obj.name);
// 调用实例方法
obj.say();

// 添加静态属性
Person.num = 666;
// 添加静态方法
Person.run = function () {
    console.log("run");
}
// 访问静态属性
console.log(Person.num);
// 调用静态方法
Person.run();

继承性

  • 把子类型中共同的属性和方法提取到父类型中
  • 较少代码的冗余度, 提升代码的复用性
  • 构造函数和构造函数之间的关系是is a关系, 那么就可以使用继承来优化代码
借用原型链实现继承

将子类的原型对象修改为父类对象,这样就能使用原型链上的属性和方法

// 父类
function Person() {
    this.name = null;
    this.age = 0;
    this.say = function () {
        console.log(this.name, this.age);
    }
}

// 子类
function Student() {
    this.score = 0;
    this.study = function () {
        console.log('Learning JavaScript');
    }
}

// 将子类的原型对象修改为父类对象,子类就拥有了父类对象的实例属性和方法
// 但由于是直接将子类原型对象修改为了父类对象,所以继承的属性值很难自定义
Student.prototype = new Person();
Student.prototype.constructor = Student;

let stu = new Student();
stu.name = 'Jason Wang';
stu.age = 18;
stu.score = 99;   
stu.say();    // Jason Wang,18
stu.study();  // Learning JavaScript

// 此种方法存在的弊端:
//    在开发中,通常需要在创建对象的时候要指定或方法的取值
//    若使用此种继承,没法在创建子类对象时给其指定属性或方法的取值
借用构造函数实现继承

在子类中调用父类构造函数,并且将父类构造函数的this修改为子类对象

function Person(myName,myAge) {
    this.name = myName;
    this.age = myAge;
    this.say = function () {
        console.log(this.name, this.age);
    }
}
function Student(myName,myAge,myScore) {
    // let stu = new Object();
    // let this = stu;
    
    // 将Person构造函数中的this改为stu
    Person.call(this,myName,myAge);  // Person.call(stu)
    this.score = myScore;
    this.study = function () {
        console.log('Learning JavaScript');
    }
}
let stu = new Student('Jason Wang',24,99);
stu.say();
stu.study();

// 若动态的父类在原型中添加方法,则子类不能使用
借用构造函数和借用原型链组合继承
  • 通过借用构造函数实现属性继承
  • 通过借用原型链实现方法继承
function Person(myName,myAge) {
    this.name = myName;
    this.age = myAge;
}
// 动态的给父类添加方法
Person.prototype.say = function () {
    console.log(this.name, this.age);
}
// 要想使用Person原型对象中的属性和方法,
// 那么就必须将Student的原型对象改为Person的原型对象才可以
Student.prototype = Person.prototype;   // 继承Person原型对象的属性和方法
Student.prototype.constructor = Student;

function Student(myName,myAge,myScore) {
    Person.call(this,myName,myAge);   // 继承Person对的属性
    this.score = myScore;
    this.study = function () {
        console.log('Learning JavaScript');
    }
}
let stu = new Student('Jason Wang',24,99);
stu.say();
继承终极方案
  • 在子类的构造函数中通过call借助父类的构造函数
  • 将子类的原型对象修改为父类的实例对象
function Person(myName,myAge) {
    this.name = myName;
    this.age = myAge;
}

Person.prototype.say = function () {
    console.log(this.name, this.age);
}

function Student(myName,myAge,myScore) {
    Person.call(this,myName,myAge);
    this.score = myScore;
    this.study = function () {
        console.log('Learning JavaScript');
    }
}
// 由于修改了Person原型对象的constructor属性, 所以破坏了Person的三角恋关系
// 由于Person和Student的原型对象是同一个, 所以给Student的元素添加方法, Person也新增方法
// Student.prototype = Person.prototype;

// 将子类的原型对象修改为父类的实例对象
Student.prototype = new Person();
Student.prototype.constructor = Student;

// 此时给Student(子类)添加一个方法
Student.prototype.run = function () {
    console.log('running');
}

let per = new Person('Jason',23);
// 父类也可以调用子类方法
// per.run();

let stu = new Student('Jason',23,100)
stu.study();  // 实例对象自己的方法
stu.say();    // 继承父类的方法
stu.run()     // 实例对象原型对象的方法

多态性

多态是指事物的多种状态

多态在编程语言中的体现
  • 父类型变量保存子类型对象,父类型变量当前保存的对象不同,产生的结果也不同,在其他编程语种中,多态需要通过继承等方式实现
  • 由于JavaScript语言是弱类型的语言,默认就是多态的,不需要继承就能实现,所以我们不用关注多态
function Dog() {
    this.eat = function () {
        console.log(" 狗吃东西");
    }
}
function Cat() {
    this.eat = function () {
        console.log(" 猫吃东西");
    }
}

function feed(animal){
    animal.eat();
}

let dog = new Dog();
feed(dog);    // 狗吃东西

let cat = new Cat();
feed(cat);    // 猫吃东西

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容