深入学习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千物皆对象(因为要除去原始值)
二 对象的分类
- 内置对象:这些对象由 JavaScript 的运行环境提供,如全局对象(如
window
和global
)、日期对象、数学对象、正则表达式对象等。 - 宿主对象:这些对象由宿主环境(如浏览器或 Node.js)提供,例如 DOM 对象和 BOM 对象。
- 自定义对象:这些对象由开发人员自己定义和创建。自定义对象可以使用构造函数、字面量表示法或者
Object.create()
方法来创建。 - 函数对象:在 JavaScript 中,函数也被看作是一个对象,因此也属于对象的一种。函数对象可以包含属性和方法,还可以接收参数并返回值。
- 原生对象:这些对象是 JavaScript 中固有的对象,如
Array
、Function
、Object
等。原生对象可以通过new
关键字或者字面量表示法来创建。
关于对象的分类,我在学习的过程中也出现了五花八门的说法,这里只是列举出其中一种分类,就不细说了
三 对象的特点
上面我们知道了js中对象有哪些,那这些对象都有什么特点呢?
- 对象由若干个属性和方法组成。属性是一种用于存储值的“容器”,而方法是一种可以执行操作的函数。
有两种方式进行访问和操作对象属性和方法:
- 点运算符:使用
.
运算符来访问对象的属性和方法。- 方括号表示法:使用方括号
[]
来访问对象的属性和方法。
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()
方法来合并 obj1
和 obj2
对象,并将结果赋值给 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关键字来调用构造函数生产对象会经历四个步骤:
- 创建一个新对象。
- 将新创建的对象设置为构造函数中的this,因此构造函数中的this就指向了新创建的对象。
- 逐行执行构造函数中的代码。
- 返回新创建的对象。
五 对象的继承
继承不止存在于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),则复制该属性值的引用地址。 实现浅拷贝的方法有很多,以下是其中的一种:
该方法遍历原对象的属性,如果该属性是原对象自身的属性,则将该属性复制到新对象中。需要注意的是,该方法只能复制原对象的一层属性,如果原对象的属性值为引用类型,则新对象和原对象会共享该引用类型的属性值。
参考文章:
JavaScript中高级进阶推荐一个JavaScript进阶深入系列专题系列(涉及原型作用域执行上下文变量对象this闭包按值传递callapplybindnew继承等
以上是关于深入学习JavaScript系列——对象/继承的主要内容,如果未能解决你的问题,请参考以下文章
深入理解JavaScript系列(46):代码复用模式(推荐篇)
深入理解JavaScript系列(17):面向对象编程之概论
深入理解JavaScript系列(42):设计模式之原型模式