[JS]JavaScript基础学习笔记(黑马pink+尚硅谷李立超)(六)

简介: [JS]JavaScript基础学习笔记(黑马pink+尚硅谷李立超)(六)

💦 原型链

  • 原型对象也有原型,这样就构成了一条原型链,根据对象的复杂程度不同,原型链的长度也不同,p对象的原型链:p对象 --> 原型 --> 原型 --> null,obj对象的原型链:obj对象 --> 原型 --> null。
  • 读取对象属性时,会优先对象自身属性,如果对象中有,则使用,没有则去对象的原型中寻找,如果原型中有,则使用,没有则去原型的原型中寻找,直到找到Object对象的原型(Object的原型没有原型(为null)),如果依然没有找到,则返回undefined
  • 作用域链,是找变量的链,找不到会报错
  • 原型链,是找属性的链,找不到会返回undefined

💦 原型的作用

所有的同类型对象它们的原型对象都是同一个,也就意味着,同类型对象的原型链是一样的。

class Person {
    name = "孙悟空"
    age = 18
    sayHello() {
        console.log("Hello,我是", this.name)
    }
}
class Dog {}
const p = new Person()
const p2 = new Person()
const d = new Dog()
const d2 = new Dog()
console.log(p === p2)
console.log(p.__proto__ === p2.__proto__)
console.log(d.__proto__ === d2.__proto__)
console.log(p.__proto__ === d.__proto__)

原型就相当于是一个公共的区域,可以被所有该类实例访问,可以将该类实例中,所有的公共属性(方法)统一存储到原型中,这样我们只需要创建一个属性,即可被所有实例访问。

在对象中有些值是对象独有的,像属性(name,age,gender)每个对象都应该有自己值,对于这些放在对象上;但是有些值对于每个对象来说都是一样的,像各种方法,对于一样的值没必要重复的创建,所以放在对象的原型对象中

const p = new Person()
const p2 = new Person()
console.log(p.__proto__)
console.log(p2.__proto__)
console.log(p.sayHello === p2.sayHello)

JS中继承就是通过原型来实现的,当继承时,子类的原型就是一个父类的实例。

class Animal{}
class Cat extends Animal{}
const cat = new Cat()
// cat --> Animal实例 --> object --> Object原型 --> null
console.log(cat.__proto__)
console.log(cat.__proto__.__proto__)
console.log(cat.__proto__.__proto__.__proto__)
console.log(cat.__proto__.__proto__.__proto__.__proto__)

function fn() {}
console.log(fn.__proto__)
console.log(fn.__proto__.__proto__)
console.log(fn.__proto__.__proto__.__proto__)

💦 修改原型

  • 大部分情况下,我们是不需要修改原型对象
  • 注意:千万不要通过类的实例去修改原型
  1. 通过一个对象影响所有同类对象,这么做不合适
  2. 修改原型先得创建实例,麻烦
  3. 危险,如果将对象的原型对象进行整个替换,该操作很危险
  • 除了通过__proto__能访问对象的原型外,还可以通过类的prototype属性,来访问实例的原型
class Person {
    name = "孙悟空"
    age = 18
    sayHello() {
        console.log("Hello,我是", this.name)
    }
}
const p = new Person()
console.log(p.__proto__)
console.log(Person.prototype)
console.log(Person.prototype === p.__proto__)
  • 修改原型时,最好通过通过类去修改
  • 好处:
  1. 一修改就是修改所有实例的原型
  2. 无需创建实例即可完成对类的修改
  • 原则:
  1. 原型尽量不要手动改
  2. 要改也不要通过实例对象去改
  3. 通过 类.prototype 属性去修改,类.prototype.属性 = 值
  4. 最好不要直接给prototype去赋值,即类.prototype = 值

💦 instanceof

  • instanceof 用来检查一个对象是否是一个类的实例
  • instanceof检查的是对象的原型链上是否有该类实例,只要原型链上有该类实例,就会返回true
  • Object是所有对象的原型,所以任何和对象和Object进行instanceof运算都会返回true
class Animal {}
class Dog extends Animal {}
const dog = new Dog()
// dog -> Animal的实例 -> Object实例 -> Object原型
console.log(dog instanceof Dog) // true
console.log(dog instanceof Animal) // true
console.log(dog instanceof Object) // true

💦 判断对象是否具有指定属性

  • in
  • 使用in运算符检查属性时,无论属性在对象自身还是在原型中,都会返回true
  • 对象.hasOwnProperty(属性名) (不推荐使用)
  • 用来检查一个对象的自身是否含有某个属性,该方法位于object原型中
  • Object.hasOwn(对象, 属性名)
  • 用来检查一个对象的自身是否含有某个属性
class Person {
    name = "孙悟空"
    age = 18
    sayHello() {
        console.log("Hello,我是", this.name)
    }
}
const p = new Person()
cconsole.log("'name' in p", 'name' in p)
console.log("'address' in p", 'address' in p)
console.log('"sayHello" in p', "sayHello" in p)
console.log('-----------------------------------')
console.log('p.hasOwnProperty("name")', p.hasOwnProperty("name")) 
console.log('p.hasOwnProperty("sayHello")', p.hasOwnProperty("sayHello"))
console.log('-----------------------------------')
console.log('Object.hasOwn(p, "name")', Object.hasOwn(p, "name"))
console.log('Object.hasOwn(p, "sayHello")', Object.hasOwn(p, "sayHello"))

🌊 旧类(早期JS中定义类)

早期JS中,直接通过函数来定义类,一个函数如果直接调用 xxx() 那么这个函数就是一个普通函数,一个函数如果通过new调用 new xxx() 那么这个函数就是一个构造函数

// 立即执行函数,防止类的代码还未完成书写就使用类进行对象的创建
var Person = (function () {
    // 相当于class中的constructor构造函数
    function Person(name, age) {
        // 在构造函数中,this表示新建的对象
        this.name = name
        this.age = age
        // 采用该方式添加方法会造成每个对象都有自己的方法,无法实现方法的共享
        // this.sayHello = function(){
        //     console.log(this.name)
        // }
    }
    // 向原型中添加属性(方法)
    Person.prototype.sayHello = function () {
        console.log(this.name)
    }
    // 类添加静态属性
    Person.staticProperty = "xxx"
    // 类添加静态方法
    Person.staticMethod = function () {}
    // 返回类
    return Person
})()
const p = new Person("孙悟空", 18)
console.log(p)
var Animal = (function(){
    function Animal(){
    }
    return Animal
})()
var Cat = (function(){
    function Cat(){
    }
    // 继承Animal
    // 继承之后,该类创建出来的对象的原型对象为父类的实例对象
    Cat.prototype = new Animal()
    return Cat
})()
var cat = new Cat()
console.log(cat)

🌊 new运算符

  1. 创建一个普通的JS对象(Object对象 {}), 为了方便,称其为新对象
  2. 将构造函数的prototype属性设置为新对象的原型
  3. 使用实参来执行构造函数,并且将新对象设置为函数中的this
  4. 如果构造函数返回的是一个非原始值,则该值会作为new运算的返回值返回(千万不要这么做),如果构造函数的返回值是一个原始值或者没有指定返回值,则新的对象将会作为返回值返回,通常不会为构造函数指定返回值
function MyClass(){
    // 1. 创建一个普通的JS对象(Object对象 {}), 为了方便,称其为新对象
    var newInstance = {}
    // 2. 将构造函数的prototype属性设置为新对象的原型
    newInstance.__proto__ = MyClass.prototype
    // 3. 使用实参来执行构造函数,并且将新对象设置为函数中的this
    this = newInstance
    // 4. 
    // 如果构造函数返回的是一个非原始值,则该值会作为new运算的返回值返回
    // return {name: 'Tom'} // 则新创建的对象为{name: 'Tom'}
    // 如果构造函数的返回值是一个原始值或者没有指定返回值,则新的对象将会作为返回值返回
    return 1 // 新创建的对象为newInstance
}
var mc = new MyClass()
console.log(mc)

🥽 数组(Array)

🌊 简介

  • 数组也是一种复合数据类型,在数组可以存储多个不同类型的数据
  • 数组中存储的是有序的数据,数组中的每个数据都有一个唯一的索引,可以通过索引来操作获取数据,索引(index)是一组大于0的整数
  • 数组中存储的数据叫做元素
  • 创建数组
  • 通过Array()来创建数组,也可以通过[]来创建数组
const arr = new Array()
const arr2 = [1, 2, 3, 4, 5] // 数组字面量
console.log(arr)
console.log(arr2)
  • 向数组中添加元素
  • 语法:数组[索引] = 元素
const arr = new Array()
console.log(arr)
arr[0] = 10 // 向数组的第一个位置添加一个数据
arr[1] = 22 // 向数组的第二个位置添加一个数据
arr[2] = 44 // 向数组的第三个位置添加一个数据
arr[3] = 88
arr[4] = 99
console.log(arr)
  • 读取数组中的元素
  • 语法:数组[索引]
  • 如果读取了一个不存在的元素,不会报错而是返回undefined
const arr = [11, 22, 33, 44, 55]
console.log(arr[0])
console.log(arr[1])
console.log(arr[10])
  • length
  • 获取数组的长度
  • 获取的实际值就是数组的最大索引 + 1
  • 向数组最后添加元素:数组[数组.length] = 元素
  • length是可以修改的
const arr = [11, 22, 33, 44, 55]
console.log(arr)
console.log(arr.length) // 获取数组的长度, 数组的最大索引 + 1
arr[arr.length] = 66 // 向数组最后添加元素
console.log(arr)
console.log(arr.length)
arr.length = 12 // 修改length
console.log(arr)
console.log(arr.length)
  • 任何类型的值都可以成为数组中的元素
// 任何类型的值都可以成为数组中的元素
let arr = [1, "hello", true, null, { name: "孙悟空" }, () => {}]
console.log(arr)

🌊 数组的遍历

遍历数组简单理解,就是获取到数组中的每一个元素

💦 for循环

//任何类型的值都可以成为数组中的元素
let arr = [1, "hello", true, null, { name: "孙悟空" }, () => {}]
console.log(arr)
// 遍历数组
for (let index = 0; index < arr.length; index++) {
    const element = arr[index];
    console.log('第' + (index+1) + '个数组元素: ', element)
}

💦 for-of语句

  • for-of语句可以用来遍历可迭代对象
  • 语法:
for(变量 of 可迭代的对象){
    语句...
}
  • 执行流程:for-of的循环体会执行多次,数组中有几个元素就会执行几次,每次执行时都会将一个元素赋值给变量
const arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧"]
// 每次执行时都会将一个元素赋值给变量
for(let value of arr){
    console.log(value)
}
// for-of语句可以用来遍历可迭代对象
for(let value of "hello"){
    console.log(value)
}

🥽 Map

  • Map用来存储键值对结构的数据(key-value)
  • Object中存储的数据就可以认为是一种键值对结构
  • Map和Object的主要区别:
  • Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,JS解释器会自动将其转换为字符串
const obj = {
    "name":"孙悟空",
    'age':18,
    [Symbol()]:"哈哈",
    [obj2]:"嘻嘻" // JS解释器会自动将其转换为字符串 object Object
}
console.log(obj)
  • Map中任何类型的值都可以成为数据的key

🌊 创建Map

语法:new Map()

// 创建一个Map
const map = new Map()
console.log(map)
// Map中任何类型的值都可以成为数据的key
// map.set(key, value) 向map中添加键值对
map.set("name", "孙悟空")
const obj2 = {}
map.set(obj2, "呵呵")
map.set(NaN, "哈哈哈")
console.log(map)

🌊 Map的属性和方法

💦 map.set(key, value) 向map中添加键值对

// 创建一个Map
const map = new Map()
console.log(map)
// Map中任何类型的值都可以成为数据的key
// map.set(key, value) 向map中添加键值对
map.set("name", "孙悟空")
const obj2 = {}
map.set(obj2, "呵呵")
map.set(NaN, "哈哈哈")
console.log(map)

💦 map.get(key) 根据key获取值

console.log(map.get("name"))
console.log(map.get(NaN))
console.log(map.get(obj2))

💦 map.size 获取map中键值对的数量

console.log(map)
console.log(map.size)

💦 map.delete(key) 删除指定数据

console.log(map)
console.log(map.size)
map.delete(NaN)
console.log(map)
console.log(map.size)

💦 map.has(key) 检查map中是否包含指定键

console.log(map.has("name"))
map.delete(NaN)
console.log(map.has(NaN))

💦 map.clear() 删除全部的键值对

console.log(map)
console.log(map.size)
map.clear()
console.log(map)
console.log(map.size)

🌊 map转换为数组

💦 Array.form()

const map = new Map()
map.set("name", "孙悟空")
map.set("age", 18)
// 将map转换为数组
// 转换的结果为二维数组
// Map的每个键值对的键和值组成一个数组作为转换结果数组的元素
const arr = Array.from(map) // [["name","孙悟空"],["age",18]]
console.log(map)
console.log(arr)

💦 扩展转换

const map = new Map()
map.set("name", "孙悟空")
map.set("age", 18)
// 将map转换为数组
// const arr = Array.from(map) // [["name","孙悟空"],["age",18]]
// 转换的结果为二维数组
// Map的每个键值对的键和值组成一个数组作为转换结果数组的元素
const arr = [...map]
console.log(map)
console.log(arr)

🌊 通过二维数组创建Map

const map2 = new Map([
    ["name", "猪八戒"],
    ["age", 18],
    [{}, () => {}],
])
// 数组中的每个一维数组为一个键值对
// 每个一维数组的第一个元素为键,第二个元素为值
console.log(map2)

🌊 遍历map

💦 for-of

// 遍历map
for (const entry of map) {
    // 获取的entry为map中的每个键值对
    // entry是一个一维数组
    // entry中第一个元素为键值对的键,第二个元素为键值对的值
    console.log(entry)
    const [key, value] = entry // 对entry解构赋值
    console.log(key, value)
}
console.log('------------------------------------')
// 在获取到map的每个键值对时直接进行解构赋值
for (const [key, value] of map) {
    console.log(key, value)
}

💦 Map.forEach()

// Map.forEach()遍历map
// 需要传递一个回调函数
// map中有几个键值对就会执行几次回调函数
// 回调函数的第一个参数为键值对的键,第二个参数为键值对的值
// 回调函数的第三个参数为map本身
map.forEach((key, value, map)=>{
    console.log(key, value, map)
})

💦 map.keys() 获取map的所有的key

console.log(map.keys()) // 获取map的所有的key
for (const key of map.keys()) {
    console.log(key)
}

💦 map.values() 获取map的所有的value

console.log(map.values()) // 获取map的所有的value 
for (const value of map.values()) {
    console.log(value)
}

💦 map.entries() 获取map的所有的键值对

console.log(map.entries()) // 获取map的所有的键值对
for (const entry of map.entries()) {
    console.log(entry)
}

相关文章
|
5天前
|
前端开发 JavaScript 区块链
连接区块链节点的 JavaScript 库 web3.js
连接区块链节点的 JavaScript 库 web3.js
|
17天前
|
JavaScript 前端开发 Go
动态加载与异步加载 JavaScript 详解:加载远程js,加载成功后执行回调函数
动态加载与异步加载 JavaScript 详解:加载远程js,加载成功后执行回调函数
|
9天前
|
JavaScript 前端开发
JavaScript-jQuery的使用 + JS的案例
JavaScript-jQuery的使用 + JS的案例
17 0
|
18天前
|
移动开发 JavaScript 前端开发
Phaser和Three.js是两个非常流行的JavaScript游戏框架
【5月更文挑战第14天】Phaser是开源的2D游戏引擎,适合HTML5游戏开发,内置物理引擎和强大的图形渲染功能,适用于消消乐等2D游戏。Three.js是基于WebGL的3D库,用于创建3D场景和应用,支持各种3D对象和交互功能,广泛应用于游戏、可视化等领域。选择框架取决于项目需求,2D选Phaser,3D选Three.js。
18 4
|
18天前
|
JSON JavaScript 前端开发
使用JavaScript和XLSX.js将数据导出为Excel文件
使用JavaScript和XLSX.js将数据导出为Excel文件
37 0
|
18天前
|
JavaScript 前端开发 开发工具
【JavaScript 技术专栏】Node.js 基础与实战
【4月更文挑战第30天】本文介绍了Node.js的基础及应用,包括事件驱动的非阻塞I/O、单线程模型和模块系统。内容涵盖Node.js的安装配置、核心模块(如http、fs、path)及实战应用,如Web服务器、文件操作和实时通信。文章还讨论了Node.js的优劣势、与其他技术的结合,并通过案例分析展示项目实施流程。总结来说,Node.js是高效后端开发工具,适合构建高并发应用,其广阔的应用前景值得开发者探索。
|
18天前
|
JSON JavaScript 前端开发
深入探讨javascript的流程控制与分支结构,以及js的函数
深入探讨javascript的流程控制与分支结构,以及js的函数
|
18天前
|
移动开发 JavaScript 前端开发
webgl学习笔记3_javascript的HTML DOM
webgl学习笔记3_javascript的HTML DOM
20 0
webgl学习笔记3_javascript的HTML DOM
|
18天前
|
JavaScript 前端开发 Java
webgl学习笔记2_javascript基础快速学习
webgl学习笔记2_javascript基础快速学习
20 0
|
18天前
|
JavaScript 前端开发 算法
< JavaScript小技巧:如何优雅的用【一行代码 】实现Js中的常用功能 >
在开发中,采用简洁的语法和结构,遵循一致的命名规范,具有良好的代码组织和注释,能很好的提高代码的质量。可读性:易于阅读和理解。清晰的命名、简洁的语法和良好的代码结构可以使代码的意图更加明确,降低理解代码的难度,提高代码的可读性。可维护性:易于维护。当代码逻辑清晰、结构简洁时,开发者可以更快速地定位和修复bug,进行功能扩展或修改。同时,可读性高的代码也有助于后续的代码重构和优化。可扩展性:更具有扩展性和灵活性。清晰的代码结构和简洁的代码风格使得添加新功能、修改现有功能或扩展代码更加容易。
< JavaScript小技巧:如何优雅的用【一行代码 】实现Js中的常用功能 >