JS函数有双重性质:new实例时是类型,直接获取属性时是对象(如:son.name)
JS函数为对象时,有3属性prototype
,__proto__
,自己定义的静态属性和方法(多个),不存在自身属性
JS函数的原型链就一个,不分是对象时还是类型时
function parent(name) {
let funName = "变量name";
}
parent.staticName = "我叫A";
parent.staticAge = "最少21岁";
parent.prototype.wife = "父亲的女人";
function son(name) {
this.info = "son属性";
this.name = name;
}
son.prototype.girl = "儿子的女朋友";
console.log("自身属性:", parent.funName); //undefined 不存在自身属性
console.log("静态属性:", parent.staticName); //我叫A
console.log("prototype:", parent.prototype); //{wife: '父亲的女人', constructor: ƒ} constructor包含了静态属性staticName
console.log("1:", parent.prototype.prototype); //undefined
console.log("__proto__:", parent.__proto__); //对象祖先类:ƒ () { [native code] }
console.log("prototype.__proto__:", parent.prototype.__proto__);
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ,...}
proto代表上一层对象空间
对象空间上的__proto__
可以修改
原型空间上的__proto__
也可以修改
console.log("parent.__proto__:", parent.__proto__); //ƒ () { [native code] }
parent.__proto__ = {};
console.log("parent.__proto__-edit:", parent.__proto__); //{}
parent.__proto__ = [];
console.log("parent.__proto__-edit:", parent.__proto__); //[]
//原型空间上的__proto__也可以修改
console.log("parent.prototype.__proto__-1:", parent.prototype.__proto__);
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, ...}
parent.prototype.__proto__ = {};
console.log("parent.prototype.__proto__-2:", parent.prototype.__proto__); //{}
//无限循环
console.log("parent:", parent); //parent的对象空间
console.log("parent:", parent.prototype); //parent的原型空间
console.log("parent:", parent.prototype.constructor); //parent的对象空间
console.log("parent:", parent.prototype.constructor.prototype); //parent的原型空间
继承
简单继承
可以看出,小A继承了实例上的funName,和原型上的 wife
但一个实例不能获取静态属性,也不能继承静态属性。静态属性 == 类属性
const LaoA = new parent("老A");
const XiaoA = new son("小A");
XiaoA.__proto__ = LaoA;
console.log("小A:", XiaoA, XiaoA.funName, XiaoA.wife);
//son {info: 'son属性', name: '小A'} 老A 父亲的女人
console.log("小A-静态属性:", XiaoA.staticAge); //undefined
中等继承
继承后,操作属性或方法,会一层层向上查找,但到原型空间查找时,原型空间有(constructor.prototype和__proto__
)
这时只走constructor.prototype 这条线去查找,所以找不到爷爷的job
但通过原型链是能找到爷爷类的
function grandfather() {}
grandfather.staticCar="奔驰"
grandfather.prototype.job = "老师";
parent.__proto__ = grandfather; //将父类的__proto__指向爷爷类
console.log("小A调用parent的对象空间:", XiaoA.__proto__.__proto__.constructor); //parent的对象空间
console.log(
"小A调用parent的上层空间:",
XiaoA.__proto__.__proto__.constructor.__proto__
); //grandfather
console.log("小A-获取爷爷的job:", XiaoA.job); //undefined
子类继承静态属性
方法一:找到父亲爷爷的静态属性,然后手动赋值给 子类静态属性
console.log("son获取静态属性:", son.staticName); //undefined
//下面循环,打印出了3个静态属性和方法
// for (let key in parent) {
// console.log("key:", key); //key:staticName, key:staticAge, key:staticCar
// }
for (let key in parent) {
//只要本构造函数的自有属性
if (Object.prototype.hasOwnProperty.call(parent, key)) {
//console.log("v:", key); //key:staticName, key:staticAge
son[key] = parent[key]; //好比son.staticX="abc" 直接写静态属性
}
}
console.log("son获取静态属性2:", son.staticName); //我叫A
方法二:parent之所以能获取爷爷的静态属性,是因为在前面执行了这段代码
parent.__proto__ = grandfather //将父类的__proto__指向爷爷类
那么直接把
son.__proto__ = parent
就搞定了,一样的道理。
方法三: (ES6方式) 用Object.setPrototypeOf()
Object.setPrototypeOf(parent, grandfather)
这行代码最终建立的关系,还是son.__proto__ = parent
之所以列举3种方式,是因为浏览器有的使用es5,有的es6,考虑到兼容。
不然直接Object.setPrototypeOf()搞定。但梳理了一遍,也就更清楚原型链是如何继承的,并继承静态属性和方法
结尾:
抛开 JavaScript 用于模拟 Java 类的复杂语法设施(如 new、Function Object、函数的 prototype 属性等)
原型系统可以说相当简单,可以用两条概括:
1、如果所有对象都有私有字段[[prototype]],就是对象的原型;
2、读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。
这个模型在 ES 的各个历史版本中并没有很大改变
ES6 提供了一系列内置函数,以便更为直接地访问操纵原型。
分别为:
Object.create 根据指定的原型创建新对象,原型可以是 null;
Object.getPrototypeOf 获得一个对象的原型;
Object.setPrototypeOf 设置一个对象的原型。