JS基础 原型与继承

Posted wgchen~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS基础 原型与继承相关的知识,希望对你有一定的参考价值。

阅读目录

原型基础

原型对象

每个对象都有一个原型 prototype 对象,通过函数创建的对象也将拥有这个原型对象。
原型是一个指向对象的指针。

  1. 可以将原型理解为对象的父亲,对象从原型对象继承来属性
  2. 原型就是对象除了是某个对象的父母外没有什么特别之处
  3. 所有函数的原型默认是 Object 的实例,所以可以使用 toString / toValues / isPrototypeOf等方法的原因。
  4. 使用原型对象为多个对象共享属性或方法
  5. 如果对象本身不存在属性或方法将到原型上查找
  6. 使用原型可以解决,通过构建函数创建对象时复制多个函数造成的内存占用问题
  7. 原型包含 constructor 属性,指向构造函数
  8. 对象包含 __proto__ 指向他的原型对象

使用数组原型对象的 concat 方法完成连接操作

下例使用的就是数组原型对象的 concat 方法完成的连接操作。

let hd = ["a"];
console.log(hd.concat("b"));
console.log(hd);

默认情况下创建的对象都有原型。

let hd =  name: "wgchen" ;
console.log(hd);

以下 x、y 的原型都为元对象 Object,即JS中的根对象

let x = ;
let y = ;
console.log(Object.getPrototypeOf(x) == Object.getPrototypeOf(y)); //true
// Object.getPrototypeOf()方法返回指定对象的原型(即内部属性的值)。[[Prototype]]

创建一个极简对象(纯数据字典对象)没有原型(原型为 null)

我们也可以创建一个极简对象(纯数据字典对象)没有原型(原型为 null)

// hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。

let hd =  name: 3 ;
console.log(hd.hasOwnProperty("name")); // true

let xj = Object.create(null, 
  name: 
    value: "wgchen"
  
);

console.log(xj.hasOwnProperty("name"));
// Uncaught TypeError: xj.hasOwnProperty is not a function

//Object.keys是静态方法,不是原型方法所以是可以使用的
console.log(Object.keys(xj));
console.log(xj);

函数拥有多个原型,prototype 用于实例对象使用,__proto__ 用于函数对象使用

function User() 

User.__proto__.view = function() 
  console.log("User function view method");
;

User.view();

User.prototype.show = function() 
  console.log("wgchen");
;

let hd = new User();
hd.show();

console.log(User.prototype == hd.__proto__);

原型关系分析,与方法继承的示例

let hd = new Object();
hd.name = "wgchen";

Object.prototype.show = function() 
  console.log("blog");
;
hd.show();

function User() 
let xj = new User();
xj.show();

User.show();

使用构造函数创建对象的原型体现

  1. 构造函数拥有原型
  2. 创建对象时构造函数把原型赋予对象

function User() 
let xj = new User();
console.log(xj.__proto__ == User.prototype); // true

下面使用数组会产生多级继承即原型链

let hd = [];
console.log(hd);
console.log(hd.__proto__ == Array.prototype);

let str = "";
console.log(str.__proto__ == String.prototype);

下面使用 setPrototypeOfgetPrototypeOf 获取与设置原型

let hd = ;
let parent =  name: "parent" ;
Object.setPrototypeOf(hd, parent);

console.log(hd);
console.log(Object.getPrototypeOf(hd));


使用自定义构造函数创建的对象的原型体现

function User() 
let hd = new User();
console.log(hd);

constructor 存在于 prototype 原型中,用于指向构建函数的引用。

function hd() 
  this.show = function() 
    return "show method";
  ;


const obj = new hd(); 
console.log(obj instanceof hd); //true

const obj2 = new obj.constructor();
console.dir(obj2.show()); //show method

使用对象的 constructor 创建对象

function User(name, age) 
  this.name = name;
  this.age = age;


function createByObject(obj, ...args) 
  const constructor = Object.getPrototypeOf(obj).constructor;
  return new constructor(...args);


let hd = new User("wgchen");
let xj = createByObject(hd, "willem", 12);

console.log(xj);

原型链

通过引用类型的原型,继承另一个引用类型的属性与方法,这就是实现继承的步骤。


使用 Object.setPrototypeOf 可设置对象的原型,下面的示例中继承关系为 obj>hd>cms

Object.getPrototypeOf 用于获取一个对象的原型。

let obj = 
  name: "wgchen"
;

let hd = 
  web: "blog"
;

let cms = 
  soft: "willem"
;

//让obj继承hd,即设置obj的原型为hd
Object.setPrototypeOf(obj, hd);
Object.setPrototypeOf(hd, cms);

console.log(obj.web); // blog
console.log(Object.getPrototypeOf(hd) == cms); //true

原型检测

instanceof 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

function A() 
function B() 
function C() 

const c = new C();
B.prototype = c;

const b = new B();

A.prototype = b;
const a = new A();

console.dir(a instanceof A); //true
console.dir(a instanceof B); //true
console.dir(a instanceof C); //true
console.dir(b instanceof C); //true
console.dir(c instanceof B); //false

使用 isPrototypeOf 检测一个对象是否在另一个对象的原型链中

const a = ;
const b = ;
const c = ;

Object.setPrototypeOf(a, b);
Object.setPrototypeOf(b, c);

console.log(b.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(a)); //true
console.log(c.isPrototypeOf(b)); //true

属性遍历

使用 in 检测原型链上是否存在属性,使用 hasOwnProperty 只检测当前对象。

let a =  url: "blog" ;
let b =  name: "wgchen" ;
Object.setPrototypeOf(a, b);

console.log("name" in a); // true
console.log(a.hasOwnProperty("name")); // false
console.log(a.hasOwnProperty("url")); // true

使用 for/in 遍历时同时会遍历原型上的属性如下例

let hd =  name: "wgchen" ;
let xj = Object.create(hd, 
  url: 
    value: "blog",
    /*
    属性四种特性之一enumerable:
    对象属性是否可通过for-in循环,或Object.keys() 读取
    */
    enumerable: true
  
);

for (const key in xj) 
  console.log(key);


hasOwnProperty 方法判断对象是否存在属性,而不会查找原型。
所以如果只想遍历对象属性使用以下代码

let hd =  name: "wgchen" ;
let xj = Object.create(hd, 
  url: 
    value: "blog",
    enumerable: true
  
);

for (const key in xj) 
  if (xj.hasOwnProperty(key)) 
    console.log(key); // url
  

借用原型 *

使用 callapply 可以借用其他原型方法完成功能。

下面的 xj 对象不能使用 max 方法,但可以借用 hd 对象的原型方法。

let hd = 
  data: [1, 2, 3, 4, 5]
;

Object.setPrototypeOf(hd, 
  max: function() 
    return this.data.sort((a, b) => b - a)[0];
  
);
console.log(hd.max()); // 5

let xj = 
  lessons:  js: 100, php: 78, node: 78, linux: 125 ,
  get data() 
    return Object.values(this.lessons);
  
;
console.log(hd.__proto__.max.apply(xj));// 125

上例中如果方法可以传参,那就可以不在 xj 对象中定义 get 方法了。

let hd = 
  data: [1, 2, 3, 4, 5]
;

Object.setPrototypeOf(hd, 
  max: function(data) 
    return data.sort((a, b) => b - a)[0];
  
);
console.log(hd.max(hd.data)); // 5

let xj = 
  lessons:  js: 100, php: 78, node: 78, linux: 125 
;
console.log(hd.__proto__.max.call(xj, Object.values(xj.lessons))); // 125

因为 Math.max 就是获取最大值的方法,所以代码可以再次优化

let hd = 
  data: [1, 2, 3, 4, 5]
;
console.log(Math.max.apply(null, Object.values(hd.data))); // 5

let xj = 
  lessons:  js: 100, php: 78, node: 78, linux: 125 
;
console.log(Math.max.apply(xj, Object.values(xj.lessons))); // 125

下面是获取设置了 class 属性的按钮,但DOM节点不能直接使用数组的 filter 等方法,但借用数组的原型方法就可以操作了。

<body>
  <button message="wgchen" class="red">wgchen but</button>
  <button message="blog">blog but</button>
</body>
<script>
  let btns = document.querySelectorAll("button");
  btns = Array.prototype.filter.call(btns, item => 
    return item.hasAttribute("class");
  );
</script>

this 不受原型继承影响

this 指向调用属性时使用的是对象。

let hd = 
  name: "wgchen"
;

let ycc = 
  name: "willem",
  show() 
    return this.name;
  
;

hd.__proto__ = ycc;
console.log(hd.show()); // wgchen

原型总结

prototype

函数也是对象,也有原型,函数有 prototype 属性指向他的原型

为构造函数设置的原型指,当使用构造函数创建对象时把这个原型赋予给这个对象

function User(name) 
  this.name = name;


User.prototype = 
  show() 
    return this.name;
  
;

let xj = new User("wgchen");
console.log(xj.show()); // wgchen

函数默认 prototype 指包含一个属性 constructor 的对象,constructor 指向当前构造函数

function User(name) 
  this.name = name;


let xj = new User("wgchen"); // User name: 'wgchen'

console.log(xj);
console.log(User.prototype.constructor == User); //true
console.log(xj以上是关于JS基础 原型与继承的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 原型与继承机制详解

JS基础-原型继承

JS基础知识——原型与原型链

[javascript]js原型链以及原型链继承

JS实现继承的几种方式

JS 类继承和原型继承区别