Skip to content

原型

心理多难受,事实如此就是事实如此,时间久了就不难受了,习惯了就好。

原型

原型的概念

js
1. 每个对象(广义)都有原型,原型也是个对象。
2. 对象可以使用原型上的属性(继承)。

如何获取对象的原型

通过对象获取原型(隐式原型获取方式):

js
对象.__proto__
隐式原型才是真正的原型。自身的原型

image-20240304100426129

通过对象的构造函数获取原型(显示原型获取方式):

js
对象的构造函数.prototype 原型的显示方式

一个对象的 __proto__属性与其构造函数的prototype属性指向同一个对象(该对象的原型对象)

image-20240311122822189

对象、构造函数、原型之间的关系

① 对象和构造函数

js
1. 构造函数是对象的描述,对象是构造函数的实例
2、一个构造函数可以有无数个对象,一个对象只能有一个构造函数

② 对象和原型

js
1. 每个对象都有原型,可以使用原型上的属性
2. 一个对象只能有一个原型,一个原型可以作为多个对象的原型。

③ 构造函数和原型

js
1. 可以通过构造函数获取到对象的原型
2. 构造函数相同的对象,原型也是相同的; 相同数据类型的原型,原型相同。

原型的应用:节省空间

1)对象和构造函数: 对象是构造函数的实例, 对象由构造函数产生。

2)对象和原型: 每个对象都有原型,可以继承原型上的属性。

3)构造函数和原型: 如果对象的构造函数相同,对象的原型也相同。

自定义构造函数时原型的应用

我们在写构造函数的时候,通常会把方法定义在它的原型上,这样可以让构造函数所有的实例化对象资源共享,节省内存。

js
// 自定义构造函数=创建一种数据类型
function User(name, age, address) {
    this.name = name;
    this.age = age;
    this.address = address;
}

// 将方法添加到 User的实例的原型
// 大部分构造函数的方法,都可以放在实例的原型中。
User.prototype.addShopcart = function(product) {
    console.log(this.name + '将' + product + '添加到购物车!');
};

User.prototype.buy = function(product) {
    console.log(this.name + '购买了' + product);
};

原型题目

js
// 创建数组
var arr01 = [10,20,30,40,50];
arr01.username = '高小乐';
arr01.__proto__.age = 100;
Array.prototype.address = '上海';

console.log(arr01.username);  // '高小乐'
console.log(arr01.age);       // 100
console.log(arr01.address);   // 上海
console.log('');


// 再创建数组
var arr02 = ['a','b','c'];
console.log(arr02.username);   //undefeind
console.log(arr02.age);        // 100
console.log(arr02.address);    // 上海
console.log('');


// 给arr02对象自身添加 address 属性
arr02.address = '北京';

console.log(arr01.address);  // 上海
console.log(arr02.address);  // 北京

判断属性是否属于对象本身

使用方法 hasOwnProperty, 每个对象都有该方法,参数是属性名, 返回布尔值。判断属性是否是对象自身的属性,自身的属性返回 true,不是自身的属性(原型上的属性或者对象自身和原型都没有的属性)返回 false。

js
对象.hasOwnProperty('属性名');
'属性名' in 对象;	in仅仅判断是否可以使用该属性
js
只有属性在对象本身上才返回true,否则都是false(包括在原型上但是不在本身)
css
// 自定义构造函数
function Person(name, age) {
  //设置对象的属性
  this.name = name;
  this.age = age;
}
// 给原型添加方法
// 把方法定义到原型上,可以节省内存
Person.prototype.say = function(){
  console.log(this); //谁调用了thisthis就指向谁。
  console.log('我是'+this.name+',我今年'+this.age+'岁了');
};


//实例化对象
var p1 = new Person('曹操', 18);

console.log(p1.hasOwnProperty('name'));  // true 自身的属性
console.log(p1.hasOwnProperty('age'));   // true 自身的属性
console.log(p1.hasOwnProperty('say'));   // false 原型上的属性
console.log(p1.hasOwnProperty('constructor'));  // false 原型上的属性
console.log(p1.hasOwnProperty('grade'));  // false 自身和原型都没有的属性

创建对象并指定原型

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的原型。

语法:

js
Object.create( proto[, propertiesObject])

参数

  • proto

    新创建对象的原型对象。如果该参数是 null,那创建的对象就是一个没有原型的对象。

  • propertiesObject

    可选。如果没有指定为 undefined。则是要添加到新创建对象的不可枚举(默认)属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。

返回值:

一个新对象,带着指定的原型对象和属性。

js
// 创建对象 原型是提取准备好的 实例化的时候将对象与原型关联
var obj1 = {};
console.log(obj1);
console.log('');


// 创建对象的同时 自己设置原型
var obj2 = Object.create([10,20,30,40]);
console.log(obj2);
console.log('');

// 创建对象的同时 自己设置原型
var obj3 = Object.create(new String('hello'));
console.log(obj3);
console.log('');


// 创建没有原型的对象
var obj4 = Object.create(null);
console.log(obj4);
js
var person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
  }
};

var me = Object.create(person);

me.name = 'Matthew'; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten

me.printIntroduction(); // expected output: "My name is Matthew. Am I human? true"

Object.create() 可以在创建新对象的同时,给对象添加属性并设置特性:

第一个参数是实例的原型,第二个参数是一个对象(设置实例的属性和属性特性)详见ES6

js

Object.create(Objec.prototype, {
   // foo 是一个常规数据属性
   foo: {
    writable: true,
    configurable: true,
    value: "hello",
  },
  // bar 是一个访问器属性
  bar: {
    configurable: false,
    get() {
      return 10;
    },
    set(value) {
      console.log("Setting `o.bar` to", value);
    },
});
js
const arr = Object.create(Array.prototype, {
            0: {
                configurable: false,
                enumerable:false,
                writable:true,
                value: '北京'
            },
            1: {
                configurable: false,
                enumerable:false,
                writable:true,
                value: '上海'
            },
            address: {
                enumerable: false,
                configurable: false,
                writable: false,
                value: '上海'
            },
            age: {
                configurable:true,
                enumerable:false,
                set: function(val) {
                    
                },
                get: function() {
                    return 100
                }
            }
        });

        console.log(arr);

原型链

原型链

任何对象都有原型对象,原型还是一个对象,既然是一个对象就会有自己的原型,那原型的原型仍然还有原型,可以依次向上找原型,直到找到一个没有原型的对象。这样就形成了一条原型链。

js
每个对象都有原型,原型还是个对象,原型也有原型,原型的原型也有原型,组成了原型链

image-20240311123056740

原型链的作用

js
1. 对象在查找找属性的时候,先从自身去找看有没有这个属性,如果有,直接使用这个属性的值。
2. 如果没有,会沿着原型链向上找,如果找到就使用这个属性的值且停止查找,如果没找到继续向上找直到原型链的终点。
3. 如果找到原型链的终点还没有找到,就返回 undefined

变量的作用域链:也是往上找。

属性查找过程

原型链描述的就是是对象查找属性或者方法的过程。

属性的查找会遵循如下过程:

1)对象在查找找属性的时候,先从自身去找看有没有这个属性,如果有,直接使用这个属性的值。

2)如果没有,会沿着原型链向上找,如果找到就使用这个属性的值且停止查找,如果没找到继续向上找直到原型链的终点。

3)如果找到原型链的终点还没有找到,就返回undefined(代表已经找到顶了)

原型链图例

image-20240311123200823

原型链和构造函数

js
1. Object、Array、自定义函数等所有的函数 的原型是 Function.prototype, Function.prototype 的构造函数是 Object
2. Function.__proto__ 等于 Function.prototype, Function 的构造函数是自己

image-20240304145159869

构造函数的原型链

image-20240304114102621

数据类型:Array,Function,Object,Number,String。广义的对象包含:Array,Function,Object。

构造函数的原型链和构造函数实例的原型链仅仅在最后的地方才汇聚。

image-20240304143051960

js
// Array 是个函数, Array的构造函数是 Function
console.log(Array);
// Function.prototype 是 Array 的原型, Function.prototype 的构造函数是 Object
console.log(Function.prototype);
// 顶层原型
console.log(Object.prototype);
console.log('');


// Object 是个函数数据类型与Array类似 Object的构造函数是 Function
console.log(Object);
// Function.prototype 是 Object 的原型, Function.prototype 的构造函数是 Object
console.log(Function.prototype);
// 顶层原型
console.log(Object.prototype);
console.log('');

// Object 是个函数 Object的构造函数是 Function
console.log(Object);
// Function.prototype 是 Object 的原型, Function.prototype 的构造函数是 Object
console.log(Function.prototype);
// 顶层原型
console.log(Object.prototype);
console.log('');


// Function 是个函数, 构造函数是  Function
console.log(Function);
// Function.prototype 是 Function 的原型, Function.prototype 的构造函数是 Object
console.log(Function.prototype);
console.log(Function.__proto__ === Function.prototype);
// 顶层原型
console.log(Object.prototype);
console.log('');



Array.prototype.username = '小乐';
Function.prototype.age = 10;
Object.prototype.address = '上海';

// arr -> Array.prototype -> Object.prototype
console.log(arr.username);      //  '小乐'
console.log(arr.age);       //  undefined
console.log(arr.address);   // '上海'
console.log('');

// Array -> Function.prototype -> Object.protytpe
console.log(Array.username);       //  undefined
console.log(Array.age);        //  10
console.log(Array.address);    //  '上海'

instanceof

用于判断对象是否是自己的构造函数。

js
对象 instanceof 构造函数
js
第二个操作数是对象自己的构造函数成立true; 第二个操作数是对象原型链上的某个对象的构造函数也成立

constructor 属性

constructor用于获取自己原型上的属性

js
默认情况:
假如对象a 的原型是 b
通常,a本身上没有 constructor属性; b 自身上会有 constructor 属性,但是给 a 准备的, 值是 a 的构造函数
所以:
a.constructor 获取的是 a 的构造函数
b.constructor 的值也是 a 的构造函数

image-20240304145056554

js
Object 的原型 Function.prototype, Function.prototype 的构造函数是 Object

Function.prototype === Function.__proto__ 原因在于Function的构造函数还是自己

image-20240304151221835

创建一个大括号相当于新建了一个新对象。

js
function User() {
}
// 修改User实例的原型
User.prototype = {
    name: 'aaaa'
};
// 实例化 创建了 User的实例
var u = new User();
console.log(u.name);   // aaaa

// 再次修改User实例的原型
User.prototype.name = 'bbb';
console.log(u.name);    // bbb 


// 重新给 User.prototype 赋值,新建了一个对象,指向发生了改变,由指向User.prototype和u.__proto__同一个对象变成了分别指向两个不同的对象,解决方法就是重新创建一个实例u2。
User.prototype = {
    name: 'ccc'
};
console.log(u.name);    // bbb

数据类型的本质

具有相同构造函数的对象,就是同一种数据类型。

判断函数的数据类型

安全的类型判断

javascript
Object.prototype.toString.call(config.data) === '[object Object]'

Object.prototype.toString.call(对象) // 返回一个 [object 对象类型]