RYF javascript笔记3


4. 面向对象编程

4.1 面向对象编程概述

4.1.1 简介

4.1.1.1 构造函数

js没有“类”,而改用构造函数作为对象的模板。

var Vehicle = function() {
    this.price = 1000;
};

构造函数是一个正常函数。但是:它使用new命令调用;函数体内部使用this代表要生成的对象实例。

4.1.1.2 new命令

new命令执行构造函数,返回一个实例对象。

使用new调用构造函数时,传多少个参数都可以,甚至可以可以省略括号。

var Car = function(p) {
    this.price = p;
};
var c1 = new Car(); //price:undefined
var c2 = new Car(1); //price:1
var c3 = new Car(1,2); //price:1
var c2 = new Car; //price:undefined

不使用new调用构造函数时,构造函数就变成了普通函数,并不会生成实例对象。而且由于this这时代表全局对象,将造成一些意想不到的结果。

var c4 = Car();  //c4:undefined

因此,为了保证构造函数必须与new命令一起使用,可以在构造函数内部使用严格模式

var Lorry = function() {
    "use strict" // 严格模式
    this.price = 1;
};
var l = Lorry(); // 无法设置未定义或 null 引用的属性“price”

或者在构造函数内部判断是否使用了new命令。

function Vehicle (p) {
  if (!(this instanceof Vehicle)) {
    return new Vehicle(p);
  } 
  this._p = p;
}
var v1 = new Vehicle(9); // Vehicle里的this指向的是new出来的对象
var v2 = Vehicle(9); // Vehicle里的this指向的是window对象

4.1.1.3 instanceof运算符

instanceof用来确定对象是否是某个构造函数的实例。

在JavaScript之中,所有对象都有对应的构造函数。

[1, 2, 3] instanceof Array // true
({}) instanceof Object // true

但是,由于原始类型的值不是对象,所以不能使用instanceof运算符判断类型。

"" instanceof String // false
1 instanceof Number // false

如果存在继承关系,那么instanceof运算符对这些构造函数都返回true。

var a = [];
a instanceof Array // true
a instanceof Object // true

4.1.2 this关键字

4.1.2.1 涵义

this是指函数当前的运行环境。

难点是:JavaScript支持运行环境动态切换。

function f(){ console.log(this.x); };
var a = {x:'a'};
var b = {x:'b'};
a.m = f;
b.m = f;
a.m() // a
b.m() // b

4.1.2.2 this的使用场合

1、全局环境
在全局环境使用this,它指的就是顶层对象window。

this === window // true 
function f() {
    console.log(this === window); // true
}

2、构造函数
构造函数中的this,指的是实例对象。

3、对象的方法
将对象的方法赋值给另一个对象,会改变this的指向。

var o1 = {m:1};
o1.f = function (){ console.log(this.m);};
o1.f() // 1

var o2 = {m:2};
o2.f = o1.f
o2.f() // 2

o1.f() // 1

将对象内部的方法赋值给一个变量,也可能改变this的指向。

var a = {
        b : {
            m : function() {
                console.log(this.p);
            },
            p : 'Hello'
        }
};
var t = a.b.m;
t(); // undefined
a.b.m(); // Hello

4.1.2.3 使用注意点

1、避免多层this
2、避免数组处理方法中的this
数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。

var o = {
    v: 'hello',
    p: [ 'a1', 'a2' ],
    f: function f() {
        this.p.forEach(function (item) {
            console.log(item); 
            console.log(this.v);
        });
    }
}
o.f(); // f函数里的item正确,但this.v是undefinded

解决问题的一种方法,是使用中间变量。

var o = {
    v: 'hello',
    p: [ 'a1', 'a2' ],
    f: function f() {
        var that = this;
        this.p.forEach(function (item) {
            console.log(this.v);
        });
    }
}

3、避免回调函数中的this
回调函数中的this往往会改变指向,最好避免使用。

4.1.3 固定this的方法

4.1.3.1 call方法

函数的call方法,可以改变指定该函数内部this的指向,然后再调用该函数。

var n = 123;
var o = { n : 456 };

function a() {
  console.log(this.n);
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(o) // 456

如果this所要指向的对象,设定为null或undefined,则等同于指向全局对象。

call方法的完整使用格式如下:

func.call(thisValue, arg1, arg2, ...)

4.1.3.2 apply方法

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。它的使用格式如下。

func.apply(thisValue, [arg1, arg2, ...])

apply的第一个参数如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组。

function f(x,y){ console.log(x+y); }
f.call(null,1,1) // 2
f.apply(null,[1,1]) // 2
f.apply(null,[1,1,1]) // 2
f.apply(null,[]) // NaN

4.1.3.3 bind方法

bind方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。它的使用格式如下。

func.bind(thisValue, arg1, arg2,...)

4.2 封装

4.2.1 prototype对象

4.2.1.1 构造函数的缺点

构造函数定义的属性和方法是属于具体对象的,无法共享。(当然了)

4.2.1.2 prototype属性的作用

js的每个对象都有一个原型对象(prototype)。

  • 定义在prototype上面的属性和方法,能被所有实例对象共享。
  • 修改prototype对象会影响所有实例对象。
  • 实例优先调用自身的属性或方法,然后才是prototype的属性或方法。

示例:

function Animal () {
}
var cat1 = new Animal();
var cat2 = new Animal();

Animal.prototype.color = "white";
cat1.color // 'white'
cat2.color // 'white'

Animal.prototype.color = "yellow";
cat1.color // 'yellow'
cat2.color // 'yellow'

cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'

4.2.1.3 原型链

从自身到原型,再到原型的原型,这样就形成了原型链。直至Object.prototype,它的原型是null,因此原型链结束。

function MyArray (){}
MyArray.prototype = new Array();

var mine = new MyArray();
mine.push(1, 2, 3);

mine.length // 3
mine instanceof Array // true

4.2.1.4 constructor属性

prototype对象有一个constructor属性,默认指向prototype对象的构造函数。

function P() {}
P.prototype.constructor === P // true

4.2.2 Object.getPrototypeOf方法

返回对象的原型。

// 空对象的原型是Object.prototype
Object.getPrototypeOf({}) === Object.prototype // true

// 函数的原型是Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true

// 假定F为构造函数,f为F的实例对象
// 那么,f的原型是F.prototype
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true

4.2.3 Object.create方法

Object.create方法用于生成新的对象,可以替代new命令。它接受一个原型对象作为参数,返回一个新对象,后者完全继承前者的属性。

var oldObj = {
    id: 123,
    "name": "Hello"
};
var newObj = Object.create(oldObj)

4.2.4 isPrototypeOf方法

isPrototypeOf方法用来判断一个对象是否是另一个对象的原型。

var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true

4.3 继承

4.3.1 proto属性

在Object对象的实例有一个非标准的proto属性,指向该对象的原型对象,即构造函数的prototype属性。

var o = new Object();
o.__proto__ === o.constructor.prototype // true
o.__proto__ === Object.getPrototypeOf(o) // true

4.3.2 属性的继承

属性有两种。一种是对象自身的原生属性,另一种是继承自原型的继承属性。

4.3.2.1 对象的原生属性

对象的原生属性,可以用Object.getOwnPropertyNames获得。

Object.getOwnPropertyNames(Date)
// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]

只获原生属性中可枚举的,用Object.keys

Object.keys(Date)// []

判断对象是否具有某个属性,使用hasOwnProperty

Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false

4.3.2.2 对象的继承属性

用Object.create创建的对象,会继承所有原型对象的属性。

var proto = { p1: 123 };
var o = Object.create(proto);
o.p1 // 123
o.hasOwnProperty("p1") // false

4.3.2.3 获取所有属性

判断一个对象是否具有某个属性(不管是自身的还是继承的),使用in运算符。

"length" in Date // true
"toString" in Date // true

可用for-in循环所有可枚举属性。

var o1 = {p1:123};
var o2 = Object.create(o1,{
    p2: { value: "abc", enumerable: true }
});
for (p in o2) {
    console.log(p);
}
// p2
// p1

4.4 JavaScript模块化编程

4.4.1 使用构造函数封装私有变量

4.4.2 IIFE封装私有变量

你可能感兴趣的:(RYF javascript笔记3)