知识体系
核心知识点
JS面向对象编程
原型与原型链
继承
面向对象
编程思想
-
面向过程-C
// 面向过程-C const nameObject = { nameList: [ {text: 'lu'}, {text: 'li'}, {text: 'zhang'}, ] } export function getUserName(obj) { const text = obj.nameList.reduce((total, item) => { // 之前累加的数据 console.log('total:', total); // 当前的数据 console.log('item:', item); return `${total.text}.${item.text}`; }); return text; } getUserName(nameObject); export function getUserSex(obj) { if(obj.hasSth) { return 'MALE'; }else { return 'FEMALE'; } } /* 面向过程-getUserName与getUserSex都是一个对数据的pipeline,只是一个单独对数据的处理过程 */
eg:
// 面向过程实例 init(); whitePlay(); repaint(); check(); blackPlay(); repaint(); check();
-
面向对象-Java
// 面向对象-Java class User { constructor(obj){ this.obj = obj; }; getName(){ this.obj.nameList.reduce((total, item) => `${total.text}.${item.text}`); }; getSex(){ if(this.obj.hasSth){ return "MALE"; }else { return "FEMALE"; } } }; const user = new User(obj); user.getName(); user.getSex(); /* 面向对象-构建了一个抽象的东西-对对象的属性与方法进行了封装 */
eg:
// 面向对象实例 const checkerBoard = new CheckerBoard(); const whitePlayer = new Player('white'); const blackPlayer = new Player('black'); whitePlayer.start(); checkerBoard.repaint(); checkerBoard.check(); blackPlayer.start(); whitePlayer.rollback();
-
React中面向对象思想
/* React中面向对象思想 */ class Button extends Component { render() { return <div></div> } } new Button().render(); function Card() { return <div></div> } Card(); // 每次render的时候函数都要执行,因此用useMemo和useCallback控制执行时机
-
-
面向切面-Spring
JS中的面向对象
JS中创建对象的三种方式:
Object.create() var bar = {} var p = new Person();
三种方式创建原型链示意图
-
Object.create / {}
-
Object.create() 与{}的区别
单原型链与多原型链的区别
// 创建 var bar = {}; var baz = Object.create({}); //原型链属性关系 bar.prototype === Object.prototype; bar.prototype.constructor === Object; Object.prototype.isPrototypeOf(bar); //两种创建方式的区别 bar.__proto__ === Object.prototype; baz.__proto__.__proto__ === Object.prototype;
-
用Object.create()创建出与var bar = {}相同效果的对象
console.log(Object.create({}));//{} // Object.create({});本身创建的结果就是一个{}, 此处产生一个原型链, // var baz = {}; 又创建了一个对象,又产生了一个原型链 // 所以经过两个原型链之后指向同一个对象 var foz = Object.create(Object.prototype); // 创建了一个单层链表的对象,通过Object.prototype foz.toString(); // 因为foz通过原型链继承了Object的属性与方法,因此可以直接调用父类Object的属性方法
-
创建null对象-null对象无原型链
/*-- 使用Object.create(null)创建的null对象是没有原型链的--*/ var foo = Object.create(null); // 此处创建了一个null对象,什么属性都没有,连原型链都没有 // 而null对象无属性无原型链,因此需要调用的话,用call借用 Object.prototype.toString.call(foo); /*-- 使用const bar = {}创建的对象是有原型链的--*/ const bar = {}; bar.toString(); /* 第一反应没反应过来的问题 */ let v = {}; Object.create(v.__proto__) === Object.create(Object.prototype);//false // 因为创建的两个对象,地址不对
-
–proto–
本质是原型链关系,JavaScript这门语言,本质设计的时候,就是原型链关系。
A.proto === B 的时候。
A.xxx, 如果 A上面没有xxx, 我可以去 B上面去找,如果B上面有,我就用 B 的。
-
-
new关键字
-
使用
function Person(name, age) { this.name = name; this.age = age; // this.e = function() {} }; // 为什么不直接挂在Person上? // 对象实例化之前就已经确定具备的能力,而直接加在Person上,实例化时每个实例重复拥有相同的能力,浪费内存 // 既然能力在实例化对象之前就具备,那么直接从原型上取就可以 Person.prototype.eat = function() { }; Person.prototype.walk = function() { }; const p = new Person("麓一", 35); // 三件事 // 1. new 创建了一个对象,这个对象的原型链,指向了构造函数的原型。 p.__proto__ === Person.prototype; // 2. Person 有个原型,原型上有个constructor, 是 Person 自己 Person.prototype.constructor === Person; // 3. p 这个对象,是构造函数构造的,p 的构造是谁呢?很显然,是 Person. p.constructor === Person; // p 要有能力 p.eat(); p.walk();
-
作用
- 创建了一个对象;
- 这个对象的原型,指向了这个 Person / Function 的prototype
- 该对象实现了 person 的方法;
- 根据一些特定情况返回对象
- 如果这个构造函数没有返回值,或者返回一个非对象类型,则new 最后返回创建的这个对象(p);
- 如果这个构造函数明确返回了一个对象,则返回这个对象;
// 实现new关键字 function newFunc(Person, ...rest) { if(typeof Person !== "function") { throw new Error('new operator function the frist param must be a function'); } var obj = Object.create(Person.prototype); var result = Person.apply(obj, rest); return result && typeof result === 'object' ? result : obj; }
实现object.create()
// 犀牛书,例 6.1 function inherit(p) { if(p === null) throw TypeError(); if(Object.create) { return Object.create(p) }; if( typeof p !== "object" && typeof p !== "function") throw TypeError(); // 此处可能需要拐个弯,其实主要的作用就是将object.create({})的多原型链改为单原型链 function f() {}; f.prototype = p; return new f(); }
-
继承
继承,是描述类和类之间的关系
原型继承
/*原型继承 */
function Parent(name) {
this.name = name;
this.nameList = ["lu", "yi"];
}
Parent.prototype.getName = function(){
console.log(this.name);
}
function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const c = new Child();
// Child没法传参,只能通过Person传参
// 如果Parent有属性是引用类型,一旦其中一个实例修改了该属性,全部实例包括父对象全部都受影响。
// 问题点演示
c.nameList.pop();
const c1 = new Child();
console.log(c1.nameList);
构造函数继承
/* 构造函数继承 */
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
console.log(this.name);
}
function Child(name) {
// 将父函数的属性都添加到Child中
Parent.call(this, name);
}
const child = new Child();
// child的原型连指向Object,但是已经把Parent函数中的属性添加过来了
// 存在的问题:无法访问原型链
组合继承
/* 组合继承 */
function Parent(name) {
this.name = name;
}
Parent.prototype.getUserName = function() {
console.log(this.name);
}
function Child(name) {
// 继承属性
Parent.call(this, name);
}
// 继承原型链方法
Child.prototype = new Parent();
Child.prototype.constructor = Child;
const ch = new Child('李四');
// 存在的问题: new Parent()的时候只想构建一个原型链的关系,并不需要再将属性初始化一遍。
/*
??将属性值重新初始化一遍会存在什么问题呢?
相比于组合继承,主要区别就是原型Parent是否带了属性字段,因为第一种是new Parent()实现的,所以属性值被初始化了。
第二种方法是直接构造原型链,所以并没有执行Parent(),所以其属性值没有初始化。
*/
组合寄生式继承
/* 组合寄生式继承 */
function Parent(name) {
this.name = name;
}
Parent.prototype.getUserName = function() {
console.log(this.name);
}
function Child(name) {
Parent.call(this, name);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const chi = new Child('王五');
组合寄生与class的区别
loose 模式,babel 进行降级编译的时候,考虑到体积等,只实现核心功能
Loose 模式应该差不多,主要是这两个区别:
Class 继承,会继承静态属性;
子类中,必须在 constructor中调用 super, 因为子类自己的 this 对象,必须先通过父类的构造函数完成。