前言
迷迷糊糊终于搞懂了js的面向对象(大概),这里简单记个笔记。
javascipt中的类
众所周知,javascript虽然可以进行面向对象编程,但其本身是没有class这一概念的。javascript使用function来实现class这一概念。
1 2 3 4 5 6
| function o1() { this.test = "aaa"; }
let a = new o1; console.log(a.test);
|
即使是有class这一关键词,也只是一个语法糖,其本质还是function
1 2 3 4 5 6 7
| class o1 { constructor() { this.test = "aaa"; } };
console.log(typeof o1);
|
继承
js的继承有多种方法实现,这里只介绍组合继承
原型链
对于一个类,它有自己的原型对象Class.prototype
,通过prototype
关键字访问
对于一个类的实例(对象),它可以通过__proto__
关键词访问对应的原型对象
而关键词constructor
是上面两个的逆过程
一个原型对象也可以通过__proto__
访问父级原型对象
总结:
prototype
类的关键词,访问对应原型对象
__proto__
对象的关键词,访问对应原型对象
constructor
对象的关键词,访问对应的类
下例是一个继承链和构造函数的组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function o1(id) { this.id = id; }; o1.prototype.sayId = function() { console.log(this.id); }
function o2(id, name) { o1.call(this, id); this.name = name; } o2.prototype = new o1(); o2.prototype.sayName = function() { console.log(this.name); } o2.prototype.constructor = o2;
|
o2.prototype.constructor = o2 ???
请看下例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function o1() { this.o1 = 1; }
function o2() { o1.call(this); this.o2 = 2; } o2.prototype = new o1;
function o3() { o2.call(this); this.o3 = 3; } o3.prototype = new o2;
let test = new o3; console.log(test.constructor == o3);
|
请猜猜最后输出什么?
最开始我认为对象的constructor应该指向自己的构造函数,所以应该是true?
但这是不对的,是false!
construstor属性也是可以继承的,这样一层一层继承导致o3继承了o1的construstor
我这样写继承是错误的,正确的方法应该是这样的,将原型的construstor重新指向自己的构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function o1() { this.o1 = 1; }
function o2() { o1.call(this); this.o2 = 2; }
o2.prototype = new o1; o2.prototype.constructor = o2;
function o3() { o2.call(this); this.o3 = 3; }
o3.prototype = new o2; o3.prototype.constructor = o3;
let test = new o3; console.log(test.constructor == o1); console.log(test.constructor == o3);
|
成员属性
private
对于private
属性的成员来说来说,它只可以在构造函数内部使用的时候进行访问。
根据let
关键词的特性(不会变量提升,可以只在块级作用域作用等),它可以作为private使用。
同时,对于私有的方法,可以直接套娃实现。这样的函数无法被实例化的对象调用,也不会被继承。
如果它可以被外部访问,那它准确来说是一个protected属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function TheFather() { let a = 1; function private_1() { console.log("I am private!"); return a; } }
function son1() { let b = 2; function private_1() { console.log("I am private!"); return b; } } son1.prototype = new TheFather; son1.prototype.constructor = son1;
let test1 = new TheFather;
let test2 = new son1;
|
public
对于public对象,它是可以被外部访问,也可以被继承的。
我们只需要用this就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function TheFather() { this.a = 1;
this.public_1 = function() { return this.a; } } TheFather.prototype.public_2 = function() { return this.a; }
function son1() { this.b = 2; this.public_3 = function() { return this.b; } } son1.prototype = new TheFather; son1.prototype.constructor = son1;
let test1 = new TheFather;
let test2 = new son1;
|
prototype
对于prototype,我们要让只有实例化的对象和子类的对象可以访问它。
我们没有具体实现的方法,可以将private属性变得可以访问即可。可以使用一个公用函数访问它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function TheFather() { let a = 1; this.private_1 = function() { return a; } }
function son1() { let b = 2; this.private_2 = function() { return b; } } son1.prototype = new TheFather; son1.prototype.constructor = son1;
let test1 = new TheFather;
let test2 = new son1;
|
原型链污染
因为js的对象继承是基于原型链的,所以如果把上一层的原型污染了就会导致下层的属性被污染。
举个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| function isObject(a) { if(typeof(a) == "object") { return true; } else { return false; } }
function merge(a, b) { for (var attr in b) { if(isObject(a[attr]) && isObject(b[attr])) { merge(a[attr], b[attr]); } else { a[attr] = b[attr]; } } return a }
function user() { this.IsAdmin = false; };
function guest() { this.check = function() { console.log("I am guest"); }; }; guest.prototype = new user;
function admin() { this.check = function() { console.log("I am admin"); }; }; admin.prototype = new user; admin.prototype.IsAdmin = true;
let P1 = new guest; let P2 = new admin;
console.log(P1.IsAdmin); console.log(P2.IsAdmin);
let o1 = JSON.parse('{"__proto__":{"IsAdmin":true}}'); merge(P1, o1); console.log(P1.IsAdmin);
|