Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript面向对象 #16

Open
liusaint opened this issue Jan 18, 2018 · 0 comments
Open

JavaScript面向对象 #16

liusaint opened this issue Jan 18, 2018 · 0 comments

Comments

@liusaint
Copy link
Owner

创建对象

//1.字面量
var obj1 = {}
//2.构造函数
function A(){}
var obj2 = new A();//{}
//3.Object.create()
var obj3 = Object.create(obj1)

构造函数与实例

//构造函数。与普通函数的区别只是调用方式。所以一般约定首字母大写。
function A(){
    this.a = 1;
    this.fnA = function(){}
}
//实例。不要忘记new。
var objA= new A();//{a: 1, fnA: ƒ}

prototype与__proto__

  • prototype。构造函数有一个属性叫prototype。即是原型对象。原型对象中的属性和方法会被所有实例共享。
  • 对象实例中有个属性__proto__指向构造函数的原型对象。
objA.__proto__ == A.prototype;//true
A.prototype.pFnA = function(){console.log(1)};
A.prototype.pA1 = 1;
objA.pFnA();//1;
objA.pA1 == 1; //true;
var objB == new A();
objB.pFnA == objA.pFnA;//true;
objB.pA1 == objA.pA1;//true;

对象中的值的查找与原型链

//在构造函数原型上加一个实例属性的同名属性。
A.prototype.a = 2;
objA.a;//1;
objA;//{a: 1, fnA: ƒ}
delete objA.a;
objA;//{fnA: ƒ},实例属性中的a已删除。
objA.a;//2 来自A.prototype;

a的查找顺序顺序:实例属性(objA)=>原型(A.prototype)。一旦找到就不会继续往上找。

//对象通用方法。
objA.hasOwnProperty;//ƒ hasOwnProperty() { [native code] }

hasOwnProperty是多数对象的公有方法。它是怎么找到的呢。
objA实例属性中没有。A.prototype中没有。
查看A.prototype,发现它中间也有一个值__prototype__。是的,prototype其实也是一个普通的对象,也是某个构造函数的实例,这个值指向它的构造函数Object()的原型,Object.prototype。在Object.prototype中找到hasOwnProperty方法。即是objA._proto_.__proto__中,这样就形成了一条原型链。

终点:

objA.haha;//undefined;

haha是实例属性中没找到,原型链中也没找到,所以返回undefined。
从上面的分析我们找到了Object.prototype中。然后继续找。发现Object.prototype.proto == null;这就是原型链的终点了。

继承

如果把构造函数看成类。根据原型链的查找原理,我们要让子构造函数的实例能调用到父类的原型中的方法和属性,要原型链查找的时候能查找到父类的prototype。子类的prototype为父类的实例或直接等于父亲的prototype。

  • 子类的prototype为父的实例。继承了父类的实例属性和原型属性
C.prototype = new P();
  • 借用构造函数。继承了加在this上的属性。
function C(){
    P1.apply(this);
    P2.apply(this);
}
  • 共享原型。继承了原型。不推荐。
C.prototype = P.prototype
  • 借用和设置原型。
function C(a,b,c,d){
    P.apply(this,arguments);
}
C.prototype = new P();
  • 临时代理函数。只继承原型。
function inheirt(C,P){
    var F = function(){}
    F.prototype = P.prototype;
    C.prototype = new F()
}
  • Object.create。
C.prototype = Object.create(P.prototype)
  • ES6 class中定义普通变量。
class C extends P {
    constructor(){
        super()
    }
    say(){
        console.log('hello');
    }
}

注:重写过后的prototype对象需要prototype.constructor = C;上面的代码都省略了。

多态

属于一个分层结构的同一个分支的对象,在发送相同的消息时(也即在被告知执行同一件事时),可通过不同方式表现出该行为。

function Animal(){}
Animal.prototype.makeSound = function(){
    console.log('animal makeSound');
}

function Dog(){}
Dog.prototype = new Animal();
Dog.prototype.makeSound = function(){
    console.log('wangwang');
}

function Duck(){}
Duck.prototype = new Animal();
Duck.prototype.makeSound = function(){
    console.log('gaga');
}

function Bird(){}
Bird.prototype = new Animal();


function makeSound(obj){
    obj.makeSound && obj.makeSound();
}

makeSound(new Dog);//wangwang
makeSound(new Duck);//gaga
makeSound(new Bird);//animal makeSound。没有重写该方法,调用父类方法。

子类的原型上的定义了与父类同名的方法,根据原型链查找的原理,会直接调用子类原型中定义的方法。如果没有自定义该方法,则调用父类方法。
在某些设计模式下,如果不允许直接调用父类的该方法。可以在父类的改方法中抛出错误,而实现类似其他语言的抽象方法的功能。

封装

把一个对象的状态和行为聚合在一起。数据和实现的隐藏。

我们发现上面无论是字面量定义的对象还是new出来的对象。它的属性都可以直接obj.a这样来读写。相当于是暴露在外的。很容易被其他程序代码修改。

JavaScript中的数据隐藏主要通过闭包来实现。我们把一些变量定义在闭包中,通过特定的函数来对其进行读写。

示例一:

function A(){
    var name = 'nameA';
    this.getName = function(){
        return name;
    }
    this.setName = function(newName){
        name = newName;
    }
}
var a = new A();
a.name;//undefined;
a.getName();//nameA
a.setName('name2');
a.getName();//name2;

示例二.所有实例中共享的私有变量:

function A(){}
A.prototype = (function(){
	var name = 'name1';
	return {
		getName:function(){
			return name;
		}
	}
})();
var a1 = new A;
var a2 = new A;
a1.getName();//name1;
a2.getName();//name1

示例三.一个简单的模块:

var module = (function() {
	//私有变量
	var privateA = '1';
	var privateB = '2';
	//私有方法。
	function A(a) {return privateA+1}
	function B() {}
	function C() {}

	//公有API。可选择暴露一些接口出去。
	return {
		A: A,
		B: B
	}
})();

另外,对于暴露出去的数据,如果是引用类型。可能出现私有性失效的问题。可通过克隆以及最低授权原则来处理。

function A(){
    var arr = [1,2,3];
    this.getArr = function(){
        return arr;
    }
}
var a = new A();
var arr = a.getArr();//[1,2,3];
arr.push(4);
a.getArr();//[1,2,3,4],私有变量被修改了。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant