
目录
02 - 方式二 : 使用 Object.setPrototypeOf方法
JavaScript当中每个对象都有一个特殊的内置属性 [[prototype]],这个特殊的对象可以指向另外一个对象

只要是对象都会有这样的一个内置属性
通过对象的 __proto__ 属性可以获取 | 设置
tip : 但是这个是早期浏览器自己添加的,存在一定的兼容性问题
- const obj = {
- name: 'star'
- };
- console.log(obj);
-
- // 可能存在兼容问题,因为是浏览器添加的属性,官方并没有加上
- // 获取对象的原型
- console.log(obj.__proto__); // 顶层Object的显式原型
-
- // 设置对象的原型
- obj.__proto__ = {info: 'aaa'}
获取 : 通过 Object.getPrototypeOf 方法可以获取
设置 : 通过 Object.setPrototypeOf 方法可以设置
- const obj = {
- name: 'star'
- };
- console.log(obj);
-
- // 官方提供的获取原型的方法,不存在兼容问题,nice
- console.log(Object.getPrototypeOf(obj)); // 顶层Object的显式原型
- // 是相同的,指向同一个对象
- console.log(obj.__proto__ === Object.getPrototypeOf(obj)); // true
-
- // setPrototypeOf 这个是官方后面加的,可能有兼容问题
- Object.setPrototypeOf(obj,{age:123})
- // 相当于
- obj.__proto__ = {age:123}
所有的函数都有一个prototype的属性(注意:不是__proto__)
箭头函数没有,箭头函数没有原型,箭头函数是ES6提出的,同时期提出的还有class,注意
虽然函数也是一个对象,但是对象上面没有prototype的属性
因为是函数,所以才有这个属性
- const obj = {}
- function foo() {}
-
- // 作用: 用来构建对象时, 给对象设置隐式原型的
- console.log(foo.prototype) // {constructor: ƒ}
- // console.log(obj.prototype) 对象是没有prototype
作用 : 用来构建对象时, 给对象设置隐式原型的
- function Foo() {
- /**
- * 1. 创建空的对象
- * 2. 将Foo的prototype原型(显式隐式)赋值给空的对象的__proto__(隐式原型) 看这里
- * 3. 将this指向该对象
- * 4. 执行函数中的代码
- * 5. 如果没有明确的返回一个非空对象, 那么this指向的对象会自动返回
- */
- }
- // 函数的显示原型
- console.log(Foo.prototype)
-
- // new操作
- const f1 = new Foo()
- const f3 = new Foo()
- // 实例的隐式原型 指向 函数的显式原型
- console.log(f1.__proto__)
- console.log(f1.__proto__ === Foo.prototype) // true
-
- // 实例的隐式原型都共同指向构造函数的显式原型
- console.log(f1.__proto__ === f3.__proto__) // true
代码
- function Student(name, age, sno) {
- this.name = name;
- this.age = age;
- this.sno = sno;
-
- // 这样也可以,但是相当于每个对象创建了自己的方法,大大占用了内存空间
- this.running = function () {
- console.log(this.name + ' running');
- };
- this.eating = function () {
- console.log(this.name + ' eating');
- };
- }
- // 每个实例对象都创建了函数,其实是没有必要的
- const s1 = new Student('star',16,1)
- const s2 = new Student('coder',17,2)
- const s3 = new Student('coderstar',18,3)

- function Student(name, age, sno) {
- // 1. 这些属性是自己的,所以写在这里
- this.name = name;
- this.age = age;
- this.sno = sno;
- }
- // 2. 函数是共享的,每个实例都需要这个函数,为了减少空间,可以把方法挂载到构造函数的显式原型上面
- Student.prototype.running = function () {
- console.log(this.name + ' running');
- };
- Student.prototype.eating = function () {
- console.log(this.name + ' eating');
- };
- // 3. 创建对象
- const s1 = new Student('star', 16, 1);
- const s2 = new Student('coder', 17, 2);
- const s3 = new Student('coderstar', 18, 3);
- // 4. 当调用方法的时候,如果自己对象中没有,会去隐式原型中查找
- // 5. 而对象的隐式原型就是函数的显示原型 s1.__proto === Student.prototype
- // 6. 所以可以使用
- s1.eating()
- s2.running()

函数的显式原型上都会存在一个属性叫做constructor,这个constructor指向当前的函数对象
- function Student(age) {
- this.age = age;
- }
-
- console.log(Student.prototype); // {constructor: ƒ} 拥有一个constructor
- console.log(Student.prototype.constructor); // ƒ Student() {}
-
- console.log(Student.prototype.constructor === Student); // true
-
- const s = new Student(17);
- console.log(s.age); // 17
- console.log(s.__proto__.constructor === Student); // // true

面向对象有三 ( 四 ) 大特性:封装、继承、多态、( 抽象 )
继承可以帮助我们将重复的代码和逻辑抽取到父类中,子类只需要直接继承过来使用即可
JavaScript当中实现继承 => 使用JavaScript原型链的机制
从一个对象上获取属性,如果在当前对象中没有获取到就会去它的原型上面获取
- const obj = {
- name: 'star',
- age: 18
- };
-
- obj.__proto__ = {};
- obj.__proto__.__proto__ = {};
- obj.__proto__.__proto__.__proto__ = { message: '找到我啦' };
-
- console.log(obj.message); // 找到我啦

主要代码 : 子类构造函数.prototype = new 父类构造函数( )
- // 1. 创建父类对象
- function Person(name, friend) {
- this.name = name;
- this.friend = friend;
- }
- Person.prototype.eating = function () {
- console.log('eating');
- };
- // 2. 创建子类对象
- function Student(age) {
- this.age = age;
- }
-
- /**
- * 错误写法
- * Student.prototype = Person.prototype
- * 这样写的话,Student类中的私有方法也会加到Person.prototype中
- */
- // 3. 给子类的显式原型赋值,赋值为父类的一个实例
- // new Peoson(),这个对象的__proto__指向Person.prototype, 构建了原型链条
- const p = new Person('ppp', [{ name: 'cobe' }])
- Student.prototype = p;
-
- /**
- * 注意 : 3 和 4 位置不能互换,否则会覆盖
- */
-
- // 4. 给子类显式原型上添加方法
- Student.prototype.studing = function () {
- console.log('studing');
- };
-
- const s = new Student(18);
- const s2 = new Student(20);
- // 可以调用父类方法
- s.eating(); // eating
- // 打印的是父类实例上的name属性,和s2的共用
- console.log(s.name); // ppp
- // 给自己的对象上增加name属性
- s.name = 'my name is star';
- // 此时才有自己的name属性
- console.log(s); // Student {age: 18, name: 'my name is star'}
- // 没有自己的name属性
- console.log(s2); // Student {age: 20}
-
- // 也就是公用同一个friend,因为是引用类型,所以数据相互串通,完美!!!
- console.log(s.friend === s2.friend); // treu
- s.friend[0].age = 10;
- console.log(s2.friend[0].age); // 10

优点:好理解,逻辑清晰,可以继承父类属性和方法
缺点:
主要代码 : 在子类构造函数中使用 call | apply 调用父类构造函数
- // 创建父类对象
- function Person(name, age, friend) {
- this.name = name;
- this.age = age;
- this.friend = friend;
- this.runing = function () {
- console.log(this.name + ' runing');
- };
- }
- // 给父类原型上添加属性
- Person.prototype.commonMessage = '我是人类';
- // 给父类原型上绑定方法
- Person.prototype.eating = function () {
- console.log(this.name + ' eating');
- };
- // 创建子类对象
- function Student(name, age, friend, sno) {
- // 至关重要的一步,用this去调父类的构造方法
- // 至关重要的一步,用this去调父类的构造方法
- Person.call(this, name, age, friend);
- this.sno = sno;
- }
-
- // 给子类显式原型上添加方法
- Student.prototype.studing = function () {
- console.log(this.name + ' studing');
- };
-
- // 1. 可以定义个性化属性
- const s1 = new Student('star', 18, [{ name: 'coder' }], 'sno0001');
- const s2 = new Student('coder', 20, [{ name: 'why' }], 'sno0002');
- console.log(s1); // Student {name: 'star', age: 18, friend: Array(1), sno: 'sno0001'}
- console.log(s2); // Student {name: 'coder', age: 20, friend: Array(1), sno: 'sno0002'}
- // 2. 调用自己类的原型方法没有问题
- s1.studing(); // star studing
- s2.studing(); // coder studing
- // 3. 可以访问父类写在构造函数中的方法
- s1.runing(); // star runing
-
- // 4. 问题来了,访问不到父类的原型上的属性和方法
- console.log(s1.commonMessage); // undefined
- s1.eating(); // 报错

优点:可以向父类传参,定制对象,且不会造成属性共享的问题
缺点:
组合继承 = 原型链继承 + 借用构造函数继承
主要代码 :
- // 创建父类对象
- function Person(name, age, friend) {
- this.name = name;
- this.age = age;
- this.friend = friend;
- this.info = 'abababab';
- }
- // 给父类原型上添加属性
- Person.prototype.commonMessage = '我是人类';
- // 给父类原型上绑定方法
- Person.prototype.eating = function () {
- console.log(this.name + ' eating');
- };
- // 创建子类对象
- function Student(name, age, friend, sno) {
- // 至关重要的第一步,用this去调父类的构造方法
- // 至关重要的第一步,用this去调父类的构造方法
- Person.call(this, name, age, friend);
- this.sno = sno;
- }
- // 至关重要的第二步,给子类的显式原型赋值,赋值为父类的一个实例
- // 至关重要的第二步,给子类的显式原型赋值,赋值为父类的一个实例
- Student.prototype = new Person();
-
- // 给子类显式原型上添加方法
- Student.prototype.studing = function () {
- console.log(this.name + ' studing');
- };
-
- // 1. 可以定义个性化属性
- const s1 = new Student('star', 18, [{ name: 'coder' }], 'sno0001');
- const s2 = new Student('coder', 20, [{ name: 'why' }], 'sno0002');
- console.log(s1); // Student {name: 'star', age: 18, friend: Array(1), info: 'abababab', sno: 'sno0001'}
- // 2. 调用自己类的原型方法没有问题
- s1.studing(); // star studing
- // 3. 调用父类的原型方法没有问题
- s1.eating(); // star runing
- // 4. 访问父类的原型上的属性没有问题
- console.log(s1.commonMessage); // 我是人类
-
- // 问题来了
- // 1. 因为是给子类的显式原型赋值,赋值为父类的一个实例,所以没有传参数,那么值都为undefined,很不优雅
- console.log(Student.prototype); // {name: undefined, age: undefined, friend: undefined, info: 'abababab', studing: ƒ}
- // 2. 调用了两次父类构造函数

优点:用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承,弥补了各自的缺点
缺点:
这里说的中间层就是 new Person ( ) 这个对象
想要创建一个优秀的中间层对象,需要满足几个条件 :
- 1. 必须创建出来一个对象
- 2. 这个对象的隐式原型必须指向父类的显式原型
- 3. 将这个对象赋值给子类的显式原型
- // 之前的做法: 但是不想要这种做法
-
- const p = new Person()
- Student.prototype = p
- const obj = {}
-
- // 可能有兼容性问题
- // obj.__proto__ = Person.prototype
- // 使用官方的定义原型的方法
- Object.setPrototypeOf(obj, Person.prototype)
-
- Student.prototype = obj
- function F() {}
- F.prototype = Person.prototype
-
- Student.prototype = new F()
- const obj = Object.create(Person.prototype)
-
- console.log(obj.__proto__ === Person.prototype) // true
-
- Student.prototype = obj
- function Person() {}
- function Student() {}
-
- // 寄生式函数
- function inherit(Subtype, Supertype) {
- // 方式二
- // 相当于 => Subtype.prototype.__proto__ = Supertype.prototype
- Object.setPrototypeOf(Subtype.prototype, Supertype.prototype)
-
- // 使得子类的显式原型上有constructor,并指向子类自己
- Object.defineProperty(Subtype.prototype, 'constructor', {
- enumerable: false,
- configurable: true,
- writable: true,
- value: Subtype
- });
- }
-
- inherit(Student, Person);
- function Person() {}
- function Student() {}
-
- // 创建对象的过程
- function createObject(o) {
- function F() {}
- F.prototype = o;
- // new F().__proto__ === Supertype.prototype
- return new F();
- }
- // 寄生式函数
- function inherit(Subtype, Supertype) {
- // 方式三
- // 相当于 Subtype.prototype => 找到new F()对象 => 通过new F().__proto__ 找到Supertype.prototype
- Subtype.prototype = createObject(Supertype.prototype);
- // 使得子类的显式原型上有constructor,并指向子类自己
- Object.defineProperty(Subtype.prototype, 'constructor', {
- enumerable: false,
- configurable: true,
- writable: true,
- value: Subtype
- });
- }
-
- inherit(Student, Person);
- function Person() {}
- function Student() {}
-
- // 寄生式函数
- function inherit(Subtype, Supertype) {
- // 方式四
- // 相当于 => Subtype.prototype.__proto__ = Supertype.prototype
- Subtype.prototype = Object.create(Supertype.prototype)
- // 使得子类的显式原型上有constructor,并指向子类自己
- Object.defineProperty(Subtype.prototype, 'constructor', {
- enumerable: false,
- configurable: true,
- writable: true,
- value: Subtype
- });
- }
-
- inherit(Student, Person);
寄生组合式继承 = 原型链继承 + 借用构造函数继承 + 寄生函数
寄生函数 : 随便拿上面的一个使用即可 , 这里使用方式四的寄生函数
- // 寄生式函数
- function inherit(Subtype, Supertype) {
- Subtype.prototype = Object.create(Supertype.prototype);
- Object.defineProperty(Subtype.prototype, 'constructor', {
- enumerable: false,
- configurable: true,
- writable: true,
- value: Subtype
- });
- }
-
- // 创建父类对象
- function Person(name, age, friend) {
- this.name = name;
- this.age = age;
- this.friend = friend;
- this.info = 'abababab';
- }
- // 给父类原型上添加属性
- Person.prototype.commonMessage = '人类';
- // 给父类原型上绑定方法
- Person.prototype.eating = function () {
- console.log(this.name + ' eating');
- };
- // 创建子类对象
- function Student(name, age, friend, sno) {
- // 至关重要的第一步,用this去调父类的构造方法
- // 至关重要的第一步,用this去调父类的构造方法
- Person.call(this, name, age, friend);
- this.sno = sno;
- }
- // 至关重要的第二步,实现子类继承父类
- // 至关重要的第二步,实现子类继承父类
- inherit(Student, Person);
-
- // 给子类显式原型上添加方法
- Student.prototype.studing = function () {
- console.log(this.name + ' studing');
- };
-
- // 1. 可以定义个性化属性
- const s1 = new Student('star', 18, [{ name: 'coder' }], 'sno0001');
- const s2 = new Student('coder', 20, [{ name: 'why' }], 'sno0002');
- console.log(s1); // Student {name: 'star', age: 18, friend: Array(1), info: 'abababab', sno: 'sno0001'}
- // 2. 调用自己类的原型方法没有问题
- s1.studing(); // star studing
- // 3. 调用父类的原型方法没有问题
- s1.eating(); // star runing
- // 4. 访问父类的原型上的属性没有问题
- console.log(s1.commonMessage); // 我是人类
-
- // 5. 连接成功
- console.log(Student.prototype); // Person {studing: ƒ, constructor: ƒ}
- // 6. 指向了自己
- console.log(Student.prototype.constructor); // Student(name, age, friend, sno) {}

优点:这是最成熟的方法,也是现在库实现的方法
缺点:我也还不知道~
相信看到这里了,都懂原型链了对吧,对吧!
什么地方是原型链的尽头 : Object的原型对象

- const obj = {
- name: 'star',
- age: '19'
- };
- obj.__proto__ = {
- address: '北京'
- };
-
- console.log(obj, obj.address); // {name: 'star', age: '19'} '北京'
对象是否有某一个属于自己的属性(不是在原型上的属性)
- // 判断是否是自己的属性,是为true
- console.log(obj.hasOwnProperty('name')); // true
- console.log(obj.hasOwnProperty('address')); // false
- // 没有的属性也为false
- console.log(obj.hasOwnProperty('abc')); // false
判断某个属性是否在某个对象或者对象的原型上
- // 只要能找到,不管在自己身上还是原型上,都返回true
- console.log('name' in obj);
- console.log('address' in obj);
- // 找不到的为false
- console.log('abc' in obj);
-
- // 注意: for in遍历不仅仅是自己对象上的内容, 也包括原型对象上的内容
- // 因为Object上的属性都是不可遍历的,所以才显示不出来,不是没有去找
-
- for (var key in obj) {
- console.log(key);
- }
用于检测构造函数(Person、Student类)的pototype,是否出现在某个实例对象的原型链上
判断对象和类之间的关系
- // instanceof用于判断对象和类(构造函数)之间的关系
- function Person() {}
- function Student() {}
- inherit(Student, Person)
-
- // stu实例(instance)对象
- var stu = new Student()
- console.log(stu instanceof Student) // true
- console.log(stu instanceof Person) // true
- console.log(stu instanceof Object) // true
- console.log(stu instanceof Array) // false
用于检测某个对象,是否出现在某个实例对象的原型链上
判断对象和对象之间的关系
- function Person() {}
- function Student() {}
- inherit(Student, Person)
-
- console.log(Student.prototype.isPrototypeOf(stu)) // true
- console.log(Person.prototype.isPrototypeOf(stu)) // true