JavaScript学习笔记——面向对象

1.面向对象编程

面向对象编程:Object Oriented Programming,即OOP,面向对象就是将真实世界的各种复杂关系抽象为一个个对象,对象之间分工合作。
面向对象的特性:封装,继承,多态(JavaScript中将多态特性称为抽象)。
在 JavaScript 中,所有数据类型都可以视为对象,当然也可以自定义对象,对象具有属性方法
自定义的对象数据类型就是面向对象中的类( Class )的概念,在JavaScript中创建对象的模板是构造函数。

2.对象的创建方式

1.var 对象名 = new Object();
创建属性和方法 对象名.属性 对象名.方法 = function() {}
var student  = new Object();
student.name = '张三'; //创建对象的属性
student.age = 12;
student.sex = '男';
//创建对象的方法
student.printName = function() {
    onsole.log('Hello! My name is ' + this.name);
}
student.printName();

2.对象字面量 var 对象名 = { 属性名:,方法 = function() {}} 
//属性与属性/方法之间使用的是逗号不是分号
var person = {
      name: '李四',
      age: 15,
      sayHello = function() {
          console.log(this.name);
      }
}
使用new Object和对象字面量的方式创建对象,代码过于冗余,而且重复性代码多,所以不适合用于创建大量对象。

3.工厂函数 function 方法名(对象的属性){
var 对象名 = new Object();
//让对象的属性和函数内的属性相等 
return 对象
} 
var 对象名 = 工厂函数名(相应属性的值);

function createPerson(name,age,sex) {
     var person = new Object();
     person.name = name,
     person.age = age,
     person.sex = sex,
     person.sayName = function() {
        console.log(this.name);
   }
     return person;
}
//使用工厂函数,要注意需要把对象返回。工厂函数使用与创建大量对象的情况,
//但是缺点在于无确定对象的具体类型。

4.构造函数创建对象
function 构造函数名(属性名) { 
this.属性 = 属性名,this,方法名 = function(){函数体} 
}  
var 对象名 = new 构造函数();
//注意:构造函数名首字母一般大写 区分普通函数
function Person(name,age,sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
      this.sayBye = function() {
             console.log('Bye Bye');
        }
 }
var p1 = new Person('张三',18,'男');
p1.sayBye();
/*
使用构造函数创建的每一个对象,都有一个constructor属性 用于指向创建实例的构造函数
判断对象的类型:typeof constructor instanceof
推荐使用instanceof,可以判断一个对象的具体类型,返回的是布尔值
*/

3.静态成员和实例成员

静态成员:直接使用对象来调用,一般工具的对象使用的是静态成员。
实例成员:构造函数中的成员就是实例成员,需要new一个构造函数对象,然后再调用,当有多个对象的时候,使用构造函数的形式创建对象。
实例也可以作为对象的另一种说法

4.原型/原型对象

引入原型/原型对象的背景:使用构造函数创建对象虽然方便,但是存在严重的内存浪费问题,构造函数内创建的方法,不同的对象调用相同的方法,就会开辟多个内存,如果实例对象很多,就会造成内存浪费。
解决构造函数内存浪费的方法:
1.可以在构造函数外部创建方法,然后在构造函数内用this.方法名 = 调用定义的方法,不过当有多个需要共享的函数时,容易造成全局变量命名冲突的问题。
2.使用原型:每一个对象都有prototype属性,每一个构造函数创建之后,都会有一个原型对象,这个对象的所有属性和方法,都会被构造函数所拥有。

//创建一个构造函数
function Student(name,number,age) {
       this.name = name;
       this.number = number;
       this.age = age;
}
//原型对象创建共享方法
Student.prototype.sayName = function() {
    console.log(this.name);
}
//多个对象 调用同一个方法 不会开辟新的内存
var stu1 = new Student('小明','001',18);
stu1.sayName();
var stu2 = new Student('小张','002',20);
stu2.sayName();

对象调用属性和方法的先后顺序:在调用属性/方法的时候,会先去调用对象本身的属性/方法,如果没有,则会去调用原型的属性/方法,如果原型也没有,则会报错。

5.prototype,proto,constructor的关系

对象有一个__proto__属性,用来指向原型对象,
构造函数调用prototype属性,创建原型对象
原型对象中有constructor属性,用来记录创建该对象的构造函数
原型对象调用prototype.constructor可以指向该对象的构造函数
prototype,__proto__,constructor三者之间的关系
     构造函数(Student) Student.prototype   创建Student的原型对象
     原型对象(Student) Student.prototype.constructor  指向构造函数
     构造函数(Student) new 一个对象    s1
     s1对象    s1.__proto__     指向Student的原型对象
     s1对象    s1.constructor   指向Student构造函数
举例:
//创建构造函数Student
function Student(name,number,age) {
       this.name = name;
       this.number = number;
       this.age = age;
}
// 共享方法 创建原型对象:Student.prototype
Student.prototype.sayName = function() {
//当改变构造函数的prototype的时候,需要重新设置constructor属性
   constructor:Student, 
   console.log(this.name);
}
//创建stu1对象
var stu1 = new Student('张三',11220,'男');
    stu1.sayName();
//对象的__proto__属性和构造函数的prototype属性都是指向原型对象 所以是相等的
    console.log(stu1.__proto__ == Student.prototype); 
//对象stu1对象的constructor属性 指向构造函数Student
    console.log(stu1.constructor == Student);

6.使用原型对象扩展内置对象

使用原型对象可以扩展内置对象的方法和属性,但是不能够修改内置对象默认的方法和属性,内置对象.prototype.方法名 = function() {}。

var arr = [15,85,96,33];
   Array.prototype.getSum = function() {
   var sum = 0;
   for(var i = 0; i < this.length; i++) {
       sum += this[i];
   }
       return sum;
}
    console.log(arr.getSum());

7.继承

一个对象继承另一个对象,那么该子对象就有父对象有的属性和方法。
JavaScript没有继承的关键字,继承的实现原理是对象的拷贝,就是将一个对象的所有成员复制给另一个对象。

//创建两个对象 parent和child
var parent = {
     name:'zs',
     age:15,
     sex:'男'
}
var child = {
     name:'ls'
}
function extend(parent,child) {
    for(var key in parent) {
       if(child[key]) {
//如果子对象存在父对象的属性或者方法 那么跳出本次循环
          continue; 
       }
       child[key] = parent[key];
   }
}
//调用方法
extend(parent,child);
console.log(child);

除了对象直接拷贝之外,还可以使用原型对象来实现继承,但是使用原型对象继承的方式,无法设置参数。

function Person() {
    this.name = 'zs';
    this.age = 18;
    this.sex = '男';
}
function Student() {
    this.score = 90;
}
//将Person()的对象,赋值给Student的原型对象
Student.prototype = new Person();
//Student()的原型对象指向Student构造函数
Student.prototype.constructor = Student;
var s1 = new Student();
console.log(s1);

bind()方法改变函数的this,返回一个新的函数,但是不会调用。

function fn(x,y) { //参数默认 this fn(this,x,y)
     console.log(this);
     console.log(x + y);
}
var nameObject = {
            name:'zs'
}
//this不是指向默认的window,而是改变之后的nameObject
var f = fn.bind(nameObject,2,5);
f(); //需要调用

call()方法:改变函数的this,同时会直接调用函数。
借用构造函数实现继承:构造函数中使用call()方法,父对象.call(this,父对象的参数);

 //父对象
function Animal(name,age,sex) {
     this.name = name;
     this.age = age;
     this.sex = sex;
}
//子对象
function Dog(name,age,sex,wow) {
//改变了Animal构造函数为当前的this(Dog),修改了参数并且直接调用函数
    Animal.call(this,name,age,sex); 
    this.wow = wow;
}
var dog = new Dog('dog',5,'母的','ww');
console.log(dog);
//借用构造函数的缺点在于不能继承父对象的函数

组合继承
组合继承:借用构造函数 + 原型继承

function Person(name,age,sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
}
//原型对象,可以实现继承父对象的方法
Person.prototype.sayHi = function() {
    console.log('Hello Everyone');
}
//借用构造函数加上call()方法,可以实现继承父类的属性
function Student(name,age,sex,score) {
    Person.call(this,name,age,sex);
    this.score = score;
}
//子对象自身特殊方法
Student.prototype.exam = function() {
     console.log('考试了');
}
//注意:不能直接将父对象的原型赋值给子对象,
//因为一个对象只有一个原型,直接赋值,会把子对象的特殊方法也复制给父对象
Student.prototype = new Person(); 
Student.prototype.constructor = Student;
var stu1 = new Student('zs',18,'男',100);
console.log(stu1);
stu1.sayHi();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值