深入学习JavaScript系列——对象/继承

Posted 十九万里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入学习JavaScript系列——对象/继承相关的知识,希望对你有一定的参考价值。

本篇为此系列第六篇

第一篇:#深入学习JavaScript系列(一)—— ES6中的JS执行上下文

第二篇:# 深入学习JavaScript系列(二)——作用域和作用域链

第三篇:# 深入学习JavaScript系列(三)——this

第四篇:# 深入学习JavaScript系列(四)——JS闭包

第五篇:# 深入学习JavaScript系列(五)——原型/原型链

第六篇: # 深入学习JavaScript系列(六)——对象/继承

第七篇:# 深入学习JavaScript系列(七)——Promise async/await generator

一 什么是对象

js中万物皆对象,这是我们刚刚接触前端便开始了解的概念,那么在js中对象到底有什么玩法呢,敢自称万物皆对象,这句话到底对不对呢?下面就通过这篇文章来探讨一下,本文只写关于对象与继承的知识点,其他的放到本系列中的其他文章。

JS中的对象是:无序属性的集合,其属性可以包含基本值、对象或者函数

这句话很抽象是不是,没关系,那我就具体展开来说:

先上总结:JavaScript中,除了原始值,都是对象

那么原始值有哪些呢?
原始值指的是没有属性或者方法的值;原始数据类型指的是拥有原始值的数据。
js定义了五种原始数据类型:

  • string
  • number
  • boolean
  • null
  • undefined

根据上面的结论,除了原始值都是对象,这里的对象我也列举出来

  • 布尔是对象(如果用 new 关键词定义)
  • 数字是对象(如果用 new 关键词定义)
  • 字符串是对象(如果用 new 关键词定义)
  • 日期永远都是对象
  • 算术永远都是对象
  • 正则表达式永远都是对象
  • 数组永远都是对象
  • 函数永远都是对象
  • 对象永远都是对象

这样看是不是就清楚很多了,然后就我们来验证一下:

var str = 'hello';
var num = 123;
var bool = true;

console.log(typeof str); // 输出 "string"
console.log(typeof num); // 输出 "number"
console.log(typeof bool); // 输出 "boolean"

str.prop = 'value';
num.prop = 'value';
bool.prop = 'value';

console.log(str.prop); // 输出 "undefined"
console.log(num.prop); // 输出 "undefined"
console.log(bool.prop); // 输出 "undefined"

使用 typeof 运算符来检查变量的类型,并尝试在原始值上添加属性。由于原始值是不可变的,因此无法为它们添加属性或方法。当我们尝试访问这些属性时,会得到 undefined 的结果

有的同学可能会说 那为什么原始值会有一些固定的方法来处理呢?是因为这些方法是js内置的,javascript 在处理原始值时会自动将其转换为对应的对象类型,使得我们可以像处理对象一样来处理原始值。

所以回到开头的问题:js万物皆对象 这句话是错误的,只是一种比喻,正确的应该是js千物皆对象(因为要除去原始值)

二 对象的分类

  1. 内置对象:这些对象由 JavaScript 的运行环境提供,如全局对象(如 windowglobal)、日期对象、数学对象、正则表达式对象等。
  2. 宿主对象:这些对象由宿主环境(如浏览器或 Node.js)提供,例如 DOM 对象和 BOM 对象。
  3. 自定义对象:这些对象由开发人员自己定义和创建。自定义对象可以使用构造函数、字面量表示法或者 Object.create() 方法来创建。
  4. 函数对象:在 JavaScript 中,函数也被看作是一个对象,因此也属于对象的一种。函数对象可以包含属性和方法,还可以接收参数并返回值。
  5. 原生对象:这些对象是 JavaScript 中固有的对象,如 ArrayFunctionObject 等。原生对象可以通过 new 关键字或者字面量表示法来创建。

关于对象的分类,我在学习的过程中也出现了五花八门的说法,这里只是列举出其中一种分类,就不细说了

三 对象的特点

上面我们知道了js中对象有哪些,那这些对象都有什么特点呢?

  1. 对象由若干个属性和方法组成。属性是一种用于存储值的“容器”,而方法是一种可以执行操作的函数。

有两种方式进行访问和操作对象属性和方法:

  • 点运算符:使用 . 运算符来访问对象的属性和方法。
  • 方括号表示法:使用方括号 [] 来访问对象的属性和方法。
var obj =  
    name: 'John',
    age: 30,
    sayHi: function() 
      console.log('Hi, my name is ' + this.name + ', and I am ' + this.age + ' years old.');
    
;
console.log(obj.name) // John
console.log(obj['age']) // 30
obj.sayHi() // Hi, my name is John, and I am 30 years old.
obj['sayHi']() // Hi, my name is John, and I am 30 years old.
// 注意看具体的使用方法,稍微有一点点不一样

2、对象可以进行增删改查操作属性或者方法。 并且属性值可以是任意js数据类型

var obj =  
    name: 'John',
    age: 30,
    sayHi: function() 
      console.log('Hi, my name is ' + this.name + ', and I am ' + this.age + ' years old.');
    
;
obj.name = 'Brian' // 改
obj.height = '174' // 增
delete obj.sayHi // 删
console.log(obj) //  name: 'Brian', age: 30, height: '174' 

添加属性或者方法也可以使用 Object.defineProperty() 看到这里应该很熟悉了吧,没错 响应式原理用的也是这个。例子如下:

let Person = 
Object.defineProperty(Person, 'name', 
   value: 'jack',
   writable: true // 是否可以改变
)
console.log(Preson)
Person.name = 'Brian'
console.log(Person) //writable为true 时name: "Brian" ;
// writable为false 时name: "jack" ;

Object.defineProperty(obj, prop, desc) 一共有三个参数, obj为需要定义属性或者方法的对象;prop为需要定义的属性名或者方法名; desc为属性值(属性描述符)

这里有个writable参数为定义后的属性或者方法是否可更改
我们熟悉的响应式写法:

get:一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined
set:一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined

let Person = 
let temp = null
Object.defineProperty(Person, 'name', 
  get: function () 
    return temp
  ,
  set: function (val) 
    temp = val
  
)
// **setter和getter函数中可以做任意复杂操作。**

这里就不展开说 我觉得Object.defineProperty()可以单独写一篇文章了 。插眼

3、查找对象的属性(方法)时,如果当前对象没有此属性(方法),会通过对象的原型和原型链依次往上查找

详情参考:

第二篇:# 深入学习JavaScript系列(二)——作用域和作用域链

第五篇:# 深入学习JavaScript系列(五)——原型/原型链

四 对象的创建

创建对象方法:
在 JavaScript 中,有多种方法可以创建对象。以下是一些常见的 JavaScript 对象创建方法:

1. 字面量表示法:使用花括号 来创建对象,并在花括号中指定对象属性和属性值,这是最简单也是最常见的:

var obj =  
  prop1: 'value1',
  prop2: 123,
  prop3: true
;

2. 构造函数:使用构造函数来创建对象,构造函数内部可以定义对象的属性和方法:

function Person(name, age) 
  this.name = name;
  this.age = age;
  this.sayHello = function() 
    console.log('Hello, my name is ' + this.name);
  


var person = new Person('John', 30);
person.sayHello(); // 输出 "Hello, my name is John"

关于构造函数,我会单独开一节来讲,这里插个眼!

3. Object.create() 方法:使用 Object.create() 方法来创建对象,该方法接收一个参数,为新对象的原型对象,

var protoObj = 
  prop1: 'value1',
  prop2: 123,
  sayHello: function() 
    console.log('Hello!');
  
;

var newObj = Object.create(protoObj);
console.log(newObj.prop1); // 输出 "value1"
newObj.sayHello(); // 输出 "Hello!"

4. 工厂函数方法:使用工厂函数来创建对象,返回一个新的对象实例,

function createPerson(name, age) 
  return 
    name: name,
    age: age,
    sayHello: function() 
      console.log('Hello, my name is ' + this.name);
    
  ;


var person = createPerson('John', 30);
person.sayHello(); // 输出 "Hello, my name is John"

5. 箭头函数:关于箭头函数的this指向问题,看本系列文章第三篇。

const person = () => ( 
  name: 'John',
  age: 30,
  sayHello() 
    console.log(`Hello, my name is $this.name`);
  
);

person().sayHello(); // 输出 "Hello, my name is John"

6. Object.assign() 方法:使用 Object.assign() 方法来创建对象,该方法可以将多个对象合并成一个新的对象,如下所示:

const obj1 =  
  prop1: 'value1',
  prop2: 123,
;

const obj2 =  
  prop3: true,
  prop4: ['a', 'b', 'c']
;

const newObj = Object.assign(, obj1, obj2);
console.log(newObj); // 输出 prop1: "value1", prop2: 123, prop3: true, prop4: Array(3)

使用 Object.assign() 方法来合并 obj1obj2 对象,并将结果赋值给 newObj。由于 Object.assign() 方法返回一个新的对象,因此原始对象不会被修改。

7. new Object() 构造函数

const obj = new Object();
obj.prop1 = 'value1';
obj.prop2 = 123;
obj.sayHello = function() 
  console.log('Hello!');
;

console.log(obj.prop1); // 输出 "value1"
obj.sayHello(); // 输出 "Hello!"

使用 new Object() 构造函数来创建了一个空对象,然后向对象添加了属性和方法。这种方式与字面量表示法创建对象的方式相似,只是使用了构造函数来创建对象。

通过new关键字来调用构造函数生产对象会经历四个步骤:

  1. 创建一个新对象。
  2. 将新创建的对象设置为构造函数中的this,因此构造函数中的this就指向了新创建的对象。
  3. 逐行执行构造函数中的代码。
  4. 返回新创建的对象。

五 对象的继承

继承不止存在于js中,面向对象的编程语言中,都有一个对象(子类)可以从另外一个对象(父类)继承属性和方法,从而减少重复的代码。

在js中 继承是通过原型和原型链的方式来实现的,每个对象都有一个指向原型对象的链接,称为原型链。当我们访问对象的属性或者方法时,如果该对象没有定义这个属性或者方法时,js就会一直沿着原型链往上查找,直到找到为止。

常见的几种继承方式:
1 原型继承
2 构造函数继承
3 组合继承
4 寄生式继承
5 寄生组合继承

原型继承

直接在原型链上写继承

function Animal ()
    this.species = 'animal';

Animal.prototype.eat = function ()
    console.log('eating')


function Cat(name,color)
    this.name = name;
    this.color = color;

 Cat.prototype = new Animal();
  var cat1 = new Cat('Fluffy','white');
    console.log(cat1.species);
  cat1.eat();
  

构造函数继承

通过构造函数来实现

unction  Animal ()
    this.species = 'animal';

function Cat(name,color)
    Animal.call(this);
    this.name = name;
    this.color = color;

var cat1 = new Cat('fluffy','white');
console.log(cat1.species); // animal

组合继承

function Animal ()
    this.species = 'animal';

Animal.prototype.eat = function ()
    console.log('eating');
;
function Cat(name,color)
    Animal.call(this);
    this.name = name;
    this.color = color;

Cat.prototype = new Animal();
Cat.prototype.constrictor = Cat
var cat1 = new Cat('Fluffy','white')
console.log(cat1.species); // animal
cat1.eat();// eating

寄生式继承

function Animal ()
    this.species = 'animal';

Animal.prototype.eat = function ()
    console.log('eating');
;
function Cat(name,color)
    Animal.call(this);
    this.name = name;
    this.color = color;

Cat.prototype = new Animal();
Cat.prototype.constrictor = Cat
var cat1 = new Cat('Fluffy','white')
console.log(cat1.species); // animal
cat1.eat();// eating

寄生组合式继承

function Animal() 
  this.species = 'animal';


Animal.prototype.eat = function() 
  console.log('eating');
;

function Cat(name, color) 
  Animal.call(this);
  this.name = name;
  this.color = color;


(function() 
  var Super = function() ;
  Super.prototype = Animal.prototype;
  Cat.prototype = new Super();
)();

var cat1 = new Cat('Fluffy', 'white');
console.log(cat1.species); // 输出:animal
cat1.eat(); // 输出:eating

六 对象的深拷贝与浅拷贝

JS对象的深拷贝实现方式有很多,以下是其中一种实现方式:

function deepCopy(obj) 
  if (typeof obj !== "object") 
    return obj;
  
  const newObj = Array.isArray(obj) ? [] : ;
  for (let key in obj) 
    if (obj.hasOwnProperty(key)) 
      newObj[key] = deepCopy(obj[key]);
    
  
  return newObj;


此函数接收一个对象参数 obj,它首先检查 obj 是否是一个对象,如果不是,则直接返回 obj。如果是一个对象,则创建一个新的空对象 newObj,如果 obj 是数组,则创建一个新的空数组;否则创建一个新的空对象。接着使用 for...in 循环遍历原对象的所有属性,如果属性是对象或数组,则递归调用 deepCopy 函数,将其复制到新对象中。最后返回新对象。 深拷贝与浅拷贝的区别在于,浅拷贝只是复制对象的引用,而深拷贝则是递归复制对象的所有属性和子属性,使得新对象与原对象完全独立,互不影响。深拷贝在处理复杂数据结构时非常有用,例如在处理嵌套的对象、数组、DOM节点等时常常需要使用深拷贝。

function shallowCopy(obj) 
  var newObj = ;
  for (var key in obj) 
    if (obj.hasOwnProperty(key)) 
      newObj[key] = obj[key];
    
  
  return newObj;

浅拷贝是指将原对象的属性值复制到新对象中,如果属性值为基本类型(number、string、boolean、null、undefined、symbol),则直接复制该属性值;如果属性值为引用类型(object、array、function),则复制该属性值的引用地址。 实现浅拷贝的方法有很多,以下是其中的一种:

该方法遍历原对象的属性,如果该属性是原对象自身的属性,则将该属性复制到新对象中。需要注意的是,该方法只能复制原对象的一层属性,如果原对象的属性值为引用类型,则新对象和原对象会共享该引用类型的属性值。

参考文章:

深入理解js对象

# JS学习—(8)JS对象

# 浅解析js中的对象

JavaScript中高级进阶推荐一个JavaScript进阶深入系列专题系列(涉及原型作用域执行上下文变量对象this闭包按值传递callapplybindnew继承等

推荐一个JavaScript进阶深入系列、专题系列,其中涉及原型、作用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等 JS 语言中的比较难懂的概念。作者写的很深入用心,详细,想要深入搞懂js基础原理得可以看这个,还不错~


原文学习地址:

JavaScript深入系列、JavaScript专题系列、ES6 系列

以上是关于深入学习JavaScript系列——对象/继承的主要内容,如果未能解决你的问题,请参考以下文章

深入理解JavaScript系列(46):代码复用模式(推荐篇)

深入理解JavaScript系列(17):面向对象编程之概论

深入理解JavaScript系列(42):设计模式之原型模式

深入理解JavaScript系列(29):设计模式之装饰者模式

深入理解JavaScript系列:强大的原型和原型链

javascript面向对象系列第三篇——实现继承的3种形式