[华为云在线课程][JavaScript的面向对象][学习笔记]

举报
John2021 发表于 2022/02/05 16:31:01 2022/02/05
【摘要】 第1章,JavaScript的面向对象机制 1.1,JavaScript的面向对象的对象JavaScript对象的概念:现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物例如,一本书,一辆汽车,一个人可以是"对象",一个数据库,一张网页一个与远程服务器的连接也可以是"对象"在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数...

第1章,JavaScript的面向对象机制

1.1,JavaScript的面向对象的对象

  • JavaScript对象的概念:

    • 现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物
    • 例如,一本书,一辆汽车,一个人可以是"对象",一个数据库,一张网页一个与远程服务器的连接也可以是"对象"
    • 在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等
    • 对象是由属性和方法组成的:
      • 属性:事物的特征,在对象中用属性来表示
      • 方法:事物的行为,在对象中用方法来表示
  • JavaScript创建对象的方式:

    • 利用构造函数创建对象
    • 利用字面量创建对象
    • 利用new Object创建对象
  • 使用对象的优点:

    • JavaScript中的对象表达结构更清晰,更强大
    • 对象是复杂数据类型object
    • 对象就是一组无序的相关属性和方法的集合
/*
        * 创建对象三种方式之一:使用对象字面量创建对象
        * {} -> 包含表这个具体事物的属性和方法,以键值对形式存在
        * 键:相当于属性名称
        * 值:相当于属性值,可以是任意值
        * */
var star = {
    "name": "hello",
    "age": "30",
    "gender": "男",
    "height": "200",
    "sayHi": function () {
        alert("hello world");
    }
}
/*
* 访问对象的属性
* 对象.属性名
* 对象['属性名'] --> 属性名在中括号中必须加引号
* 对象.方法名() --> 方法名后面必须加上()
* */
console.log(star.name);//hello
console.log(star['age']);//30
console.log(star.sayHi());//hello world
/*
        * 创建对象三种方式之二:利用 new Object 创建对象
        * */
var hello = new Object();
hello.name = "hello's name";
hello.age = "10";
hello.sayHi = function () {
    console.log("function saiHi()");
}
/*
        * 创建对象三种方式之三:利用构造函数创建对象
        * 提取对象中的一些公共的属性和方法,封装到这个函数里
        * 构造函数名首字母要大写
        * */
function 构造函数名(形参1, 形参2, 形参3) {
    this.属性名1 = 参数1;
    this.属性名2 = 参数2;
    this.属性名3 = 参数3;
}

var obj = new 构造函数名(实参1, 实参2, 实参3);

1.2,JavaScript的构造函数

  • JavaScript构造函数
    • 一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new运算符一起使用,我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面
    • 利用构造函数创建对象
    • 构造函数的语法格式:
function 构造函数名() {
    this.属性 =;
    this.方法 = function () {
    }
}

new 构造函数名();
  • JavaScript构造函数和对象案例:

    • 构造函数:如Person(),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class)
    • 构造对象:如new Person(),特指某一个对象,通过new关键字创建对象的过程我们也称为对象实例化
  • 只有以new关键字来调用的时候,我们才能说这是一个构造函数

  • 构造函数的执行过程:

    1. 当以new关键字调用时,会创建一个新的内存空间
    2. 函数体内部的this执行该内存
    3. 执行函数体内部的代码
    4. 默认返回我们的this
function Person(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.info = function () {
        console.log("姓名是:" + this.name + ",年龄是:" + this.age + ",性别是:" + this.gender);
    };
}

var person = new Person("hello", 10, "男");
console.log(person.name);//hello
person.info();//姓名是:hello,年龄是:10,性别是:男

1.3,JavaScript的new命令

  • new命令在执行时会做四件事情:
    • 在内存中创建一个新的空对象
    • 让this指向这个新的对象
    • 执行构造函数里面的代码,给这个新对象添加属性和方法
    • 返回这个新对象(所以构造函数里面不需要return)
    • JavaScript的new命令案例:
//构造函数
function Stu(name, age, address) {
    this.name = name;
    this.age = age;
    this.address = address;
    this.showInfo = function () {
        console.log("姓名:" + this.name + ",年龄:" + this.age + ",地址:" + this.address);
    };
}

//利用new来调用我的构造函数
var stu = new Stu("张三", 10, "地球");//创建一个新的内存
stu.showInfo();//姓名:张三,年龄:10,地址:地球
  • JavaScript利用字面量创建对象:
    • 对象字面量:就是花括号{}里面包含了表达这个具体事物(对象)的属性和方法
    • 对象的调用:
      • 对象里面的属性调用:对象.属性名,这个小点.就理解为"的"
      • 对象里面属性的另一种调用方式:对象[‘属性名’],注意方括号里面的属性必须加引号
      • 对象里面的方法调用:对象.方法名(),注意这个方法名后面一定加括号
//创建一个Object对象,简写
var p1 = {};
//动态增加属性、方法
p1.name = "hello";
p1.age = 10;
p1.sayHi = function () {
    alert("function: " + p1.name);
};
p1.sayHi();
  • JavaScript利用new Object创建对象
//利用new Object创建对象
//创建了一个空的对象
var obj = new Object();
obj.name = "hello";
obj.age = 1;
obj.gender = "男";
obj.sayHi = function () {
    console.log("world, " + obj.name);
};
console.log(obj.name);//hello
console.log(obj["age"]);//1
obj.sayHi();//world, hello
  • JavaScript面向对象之封装
    • 封装就是把抽象出来的属性和对属性的操作封装在一起,属性被保护在内部,程序的其他部分只有通过被授权的操作函数,才能对属性进行操作
function Person(_name, _age, _salary) {
    //Person类的公开属性,类的公开属性的定义方式是:"this.属性名"
    this.name = _name;
    //Person类的私有属性,类的私有属性的定义方式是:"var 属性名"
    var age = _age;
    var salary = _salary;
    //定义Person类的公开方法(特权方法),类的公开方法的定义方式是:"this.functionName=function(){...}"
    this.Show = function () {
        //在公开方法里面访问类的私有属性是允许的
        console.log("Age=" + age + "\t" + "Salary=" + salary);
    };

    /*
    * 定义Person类的私有方法(内部方法)
    * 类的私有方法的定义方式是:”function functionName(){...}“
    * 或者var functionName = function(){...}
    * */
    function privateFn() {
        console.log("我是Person类的私有函数privateFn");
    }

    var privateFn2 = function () {
        console.log("我是Person类的私有函数privateFn2");
    };
}

var p1 = new Person("hello", 1, 100);
//访问公有属性,这是可以正常访问的
console.log("p1.Name=" + p1.name);//p1.Name=hello
//不能使用类的对象去直接访问类私有属性,这是访问不了的,结果都是undefined
console.log("p1.Age=" + p1.age + "\t" + "p1.Salary=" + p1.salary);//p1.Age=undefined	p1.Salary=undefined
//调用类的公共函数,这是允许的
p1.Show();//Age=1 Salary=100
//不能使用类的对象去调用类的私有方法,这里会报错对象不支持此属性或者方法""
console.log("p1.privateFn():" + p1.privateFn() + "p1.privateFn2():" + p1.privateFn2());//Uncaught TypeError: p1.privateFn is not a function

1.4,JavaScript的原型对象

  • JavaScript中ES5中没有类Class,但是它取了一个新的名字叫"原型对象",因此"类=原型对象"
  • 类(原型对象)是抽象,是概念的,代表一类事物;对象是具体的,实际的,代表一个具体的事物
  • 每个对象一定会有一个原型对象
  • 原型对象实际是构造实例对象的构造器中的一个属性,只不过这个属性是个对象
  • 这个原型对象中的属性与方法,都会被对象实例所共享(类似Java中的类方法,类属性)
  • 原型对象的属性不是对象实例的属性,只要修改原型对象上的属性和方法,变动就会立刻体现在所有对象实例上
  • JavaScript对每个创建的对象都会设置一个属性__proto__,指向它的原型对象xxx.prototype
  • JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象:
    • 函数的prototype指向了一个对象,而这个对象正是调用构造函数时创建的实例的原型,也就是person1和person2的原型
    • 原型的概念:每一个JavaScript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中"继承"属性。
function Person(age) {
    this.age = age;
}

Person.prototype.dname = "hello";
var person1 = new Person();
var person2 = new Person();
console.log(person1.dname);//hello
console.log(person2.dname);//hello
  • JavaScript中,每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型:
function Person() {

}

var person1 = new Person();
var person2 = new Person();
console.log(person1.__proto__ === Person.prototype);//true
console.log(person1.__proto__ === person2.__proto__);//true
  • JavaScript中,每个原型都有一个constructor属性,指向该关联的构造函数
function Person() {

}

console.log(Person === Person.prototype.constructor);//true
/*
        * in操作符
        * 只要对象或者原型有一地方存在这个属性,就会返回true
        * */
function Person() {

}

Person.prototype.name = "hello";
var p1 = new Person();
p1.sex = "男";
alert("sex" in p1);//对象本身添加的 所以会返回true
alert("name" in p1);//原型中存在,所以会返回true
alert("age" in p1);//对象原型中都不存在,所以会返回false
function Person() {

}

Person.prototype.name = "hello";
var p1 = new Person();
p1.sex = "男";

//定义一个函数去判断原型所在的位置
function propertyLocation(obj, prop) {
    if (!(prop in obj)) {
        alert(prop + "属性不存在");
    } else if (obj.hasOwnProperty(prop)) {
        alert(prop + "该属性存在此对象中");
    } else {
        alert(prop + "该属性存在此对象的原型中");
    }
}

propertyLocation(p1, "age");//age属性不存在
propertyLocation(p1, "name");//name该属性存在此对象的原型中
propertyLocation(p1, "sex");//sex该属性存在此对象中

第2章,JavaScript的面对对象继承

  • 继承
    • 继承是面向对象语言的一个重要概念
    • 许多面向对象语言都支持两种继承方式:接口继承和实现继承;接口继承只继承方法签名,而实现继承则继承实际的方法
    • 由于函数没有签名,所以ECMAScript只支持实现继承,而实现继承主要是依靠原型链来实现的

2.1,JavaScript原型继承

  • 原型继承
    • 基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法
    • 核心:将父类的实例作为子类的原型
    • 缺点:父类新增原型方法/原型属性,子类都能够访问到,并且父类一变其他都变了
function Animal() {
    this.species = "动物";
    this.colors = ["白色"];
}

function Cat(name, eat) {
    this.name = name;
    this.cat = eat;
}

Cat.prototype = new Animal();
var cat1 = new Cat("猫", "老鼠");
console.log(cat1.species);//动物
console.log(cat1.colors);//['白色']
/*
        * 原型继承存在的问题:
        * Cat的所有的实例都会共享colors属性
        * */
function Animal() {
    this.species = "动物";
    this.colors = ["白色"];
}

function Cat(name, eat) {
    this.name = name;
    this.eat = eat;
}

Cat.prototype = new Animal();
var cat1 = new Cat("猫", "老鼠");
console.log(cat1.species);//动物
console.log(cat1.colors);//['白色']
cat1.colors.push("黑色");
console.log(cat1.colors);//['白色', '黑色']
var cat2 = new Cat("大猫", "鱼");
console.log(cat2.colors);//['白色', '黑色']
  • 构造函数继承:
    • 基本思想:在子类型构造函数的内部调用超类型构造函数。函数只不过是在特定环境中执行代码的对象,因此可通过使用call()和apply()在新创建的对象上执行构造函数
    • 因为属性是绑定在this上面的,所以调用的时候才赋值到了相应的实例中,各个实例的值就不会相互影响了
    • 核心:使用父类的构造函数来增强子类的实例,等于是赋值父类的实例属性给子类(没有用到原型)
    • 缺点:方法都在构造函数中定义,只能继承父类的实例属性和方法,不能继承原型属性/方法,无法实现函数的复用,每一个子类都有父类实例函数的副本,影响性能
function Animal() {
    this.species = "动物";
    this.colors = ["白色"];
}

function Cat(name, eat) {
    Animal.apply(this, arguments);
    this.name = name;
    this.eat = eat;
}

var cat1 = new Cat("猫", "鱼");
//动物
console.log(cat1.species);//动物
cat1.colors.push("黑色");
console.log(cat1.colors);//['白色', '黑色']
var cat2 = new Cat("狗", "狗粮");
console.log(cat2.colors);//['白色']
/*
        * 构造继承存在问题:
        * 1,方法都在构造函数中定义,所以没法利用函数的复用
        * 2,并且在超类型的原型中定义的方法对于子类型而言是不可见的
        * */
function Animal() {
    this.species = "动物";
    this.colors = ["白色"];
}

Animal.prototype.getColor = function () {
    return this.colors;
};

function Cat(name, eat) {
    Animal.apply(this, arguments);
    this.name = name;
    this.eat = eat;
}

var cat1 = new Cat("猫", "鱼");
console.log(cat1.species);//动物
cat1.colors.push("黑色");
console.log(cat1.colors);//['白色', '黑色']
console.log(cat1.getColor());
var cat2 = new Cat("狗", "狗粮");

2.2,JavaScript的组合继承

  • 基本思想:使用原型链实现对原型属性和方法的继承,而通过构造函数实现对实例属性的继承
  • 组合继承是最常用的继承方法
  • 核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后再通过将父类实例作为子类原型,实现函数复用
  • 缺点:调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那一份给屏蔽)
function Animal(species) {
    this.species = species;
    this.colors = ["白色"];
}

Animal.prototype.getColor = function () {
    console.log(this.colors);
};

function Cat(name, eat) {
    this.name = name;
    this.eat = eat;
    Animal.call(this, name);
}

//继承方法
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.sayName = function () {
    console.log(this.name);
};

var cat1 = new Cat("猫", "吃鱼");
cat1.colors.push("黑色");
console.log(cat1.colors);//['白色', '黑色']
cat1.getColor();//['白色', '黑色']
cat1.sayName();//猫
var cat2 = new Cat("波斯猫", "吃猫粮");
console.log(cat2.colors);//['白色']
cat2.getColor();//['白色']
cat2.sayName();//波斯猫
  • JavaScript的面向对象的多态:
    • 同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果
var makeSound = function (person) {
    if (person && person.sound instanceof Function) {
        person.sound();
    }
};
var Student = function () {
};
Student.prototype.sound = function () {
    console.log("学生通过发出声音练习英语");
};
var Employee = function () {
};
Employee.prototype.sound = function () {
    console.log("员工通过发出声音沟通工作");
};
makeSound(new Student());//学生通过发出声音练习英语
makeSound(new Employee());//员工通过发出声音沟通工作

2.3,JavaScript的This关键字

  • 普通函数中的this
    • 单独使用this,指的是全局对象,在浏览器中,window就是全局对象,在严格模式下,单独使用this指向的还是全局
    • 在函数中使用this,函数的所属者就会被绑定在this上,在浏览器中,window就是全局对象,严格模式就没有绑定在全局上,this就是undefined
//在es5中
function foo() {
    //这里的this是window
    console.log(this);
}

foo();//Window {window: Window, self: Window, document: document, name: '', location: Location,…}
//在es5中
function foo() {
    'use strict';
    //这里的this是undefined
    console.log(this);
}

foo();//undefined
  • 对象方法中this
    • 在对象中使用this,指向的就是我们的对象
var obj = {
    info: "tom",
    showInfo: function () {
        //这里的this是obj
        console.log(this.info);
    }
};
obj.showInfo();//tom
  • 构造函数中this
    • 在构造函数中使用,指向具体的某个实例
function Foo(name, age) {
    //这里的this指的是具体的某个实例f1或f2
    this.name = name;
    this.age = age;
    this.showName = function () {
        //这里的this指的是具体的某个实例f1或f2
        console.log(this.name);
    };
}

var f1 = new Foo("hello", 1);
f1.showName();//hello
var f2 = new Foo("world", 2);
f2.showName();//world
  • 面向对象中的this
    • 指向的是一个构造函数创建的实例,原型里面就是指通过原型所属构造函数创建出来的实例
function Foo(info) {
    //这里的this指的是该构造函数创建出来的实例,这个this和原型中的this是同一个指向
    this.info = info;
}

Foo.prototype.showInfo = function () {
    //原型里面的this指的是通过原型所属的构造函数创建出来的实例
    console.log(this.info);
};
var f1 = new Foo("hello");
f1.showInfo();//hello
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。