[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)
}

相关文章
|
29天前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
1月前
|
JavaScript 前端开发
Moment.js与其他处理时间戳格式差异的JavaScript库相比有什么优势?
Moment.js与其他处理时间戳格式差异的JavaScript库相比有什么优势?
|
1月前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API
使用JavaScript和Node.js构建简单的RESTful API
|
2月前
|
人工智能 JavaScript 前端开发
使用Node.js模拟执行JavaScript
使用Node.js模拟执行JavaScript
29 2
|
2月前
|
消息中间件 JavaScript 前端开发
用于全栈数据流的 JavaScript、Node.js 和 Apache Kafka
用于全栈数据流的 JavaScript、Node.js 和 Apache Kafka
50 1
|
2月前
|
移动开发 JavaScript 前端开发
【JavaScript】JS执行机制--同步与异步
【JavaScript】JS执行机制--同步与异步
28 1
|
2月前
|
JavaScript 前端开发
电话号码正则表达式 代码 javascript+html,JS正则表达式判断11位手机号码
电话号码正则表达式 代码 javascript+html,JS正则表达式判断11位手机号码
129 1
|
3月前
|
JavaScript 前端开发 API
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
Vue学习笔记3:对比纯JavaScript和Vue实现数据更新的实时视图显示
|
2月前
|
Web App开发 JavaScript 前端开发
Node.js:JavaScript世界的全能工具
Node.js:JavaScript世界的全能工具
|
2月前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API服务器
【10月更文挑战第12天】使用JavaScript和Node.js构建简单的RESTful API服务器
26 0