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

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

💦 提升的目的

将函数的声明定于与变量的声明进行提升,可以提前为函数和变量预留内存空间,预留比实际所需略大的内存空间,减少内存重新分配的次数,从而提高性能。

🌊 立即执行函数(IIFE)

在开发中应该尽量减少直接在全局作用域中编写代码,在全局作用域声明的变量容易被修改或覆盖,所以我们的代码要尽量编写的局部作用域。

如果使用let声明的变量,可以使用{}来创建块作用域,不同块级作用域的同名变量不会造成干扰,var声明的变量不具有块级作用域,使用var关键字声明的不同块级作用域下的同名变量会互相影响。如果使用var声明的变量,可以使用函数作用域

创建一个只执行一次的匿名函数,立即执行函数(IIFE),立即执行函数是一个匿名的函数,并它只会调用一次,可以利用IIFE来创建一个一次性的函数作用域,避免变量冲突的问题

// 括号内的函数虽然为使用function创建的函数,但是没有函数名
// 所以该函数不会被提升
// 以括号开头的函数不会被提升
(function(){
    let a = 10
    console.log(111)
}()); 
// 如果没有添加分号会两个立即执行函数会被解释器认为(...)(...)
// Js解析器自动添加的分号会在第二个立即执行函数后面 (...)(...);
// 为了避免报错,可以手动添加分号
// (intermediate value)(intermediate value)(...) is not a function
// 调用立即执行函数的括号可以写在外面也可以写在里面
(function(){
    let a = 20
    console.log(222)
})()

🌊 this

函数在执行时,JS解析器每次都会传递进一个隐含的参数,这个参数就叫做 this,this会指向一个对象,this所指向的对象会根据函数调用方式的不同而不同。

以函数形式调用时,this指向的是window;以方法的形式调用时,this指向的是调用方法的对象

function fn() {
    console.log(this === window)
    console.log("fn打印", this)
}
const obj = { name: "孙悟空" }
obj.test = fn
const obj2 = { name: "猪八戒", test: fn }
fn()
// 方法中的this为调用方法的对象
// function声明的函数为window对象的方法,直接调用函数相当于window.fn()
// 相当于调用window上的方法,所以以函数形式调用时,this指向的是window
window.fn() 
obj.test() // {name:"孙悟空"}
obj2.test() // {name:"猪八戒", test:fn}

通过this可以在方法中引用调用方法的对象,从而在方法中可以使用对象的属性或方法。

const obj3 = {
    name: "沙和尚",
    sayHello: function () {
        console.log(this.name)
    },
}
const obj4 = { 
    name: "唐僧",
    sayHello: function(){
        console.log(this.name)
    }
}
// 为两个对象添加一个方法,可以打印自己的名字
obj3.sayHello()
obj4.sayHello()

🌊 箭头函数的this

  • 箭头函数的语法:
  • ([参数]) => 返回值
  • 无参箭头函数:() => 返回值
  • 一个参数的:a => 返回值
  • 多个参数的:(a, b) => 返回值
  • 只有一个语句的函数:() => 返回值
  • 只返回一个对象的函数:() => ({...})
  • 有多行语句的函数:
() => {
    ....    
    return 返回值
}
  • 箭头函数没有自己的this,它的this有外层作用域决定
  • 箭头函数的this和它的调用方式无关
function fn() {
    console.log("fn -->", this)
}
// 箭头函数没有自己的this,它的this有外层作用域决定
// 由于该箭头函数声明在全局中,所以该箭头函数的this总是window
const fn2 = () => {
    console.log("fn2 -->", this)
}
fn() // window
fn2() // window
const obj = {
    name:"孙悟空",
    fn, // fn:fn 属性名和属性值一样可以简写
    fn2, // 箭头函数fn2声明在全局作用域中,this指向window
    sayHello(){
        console.log(this.name)
        function t(){
            console.log("t -->", this)
        }
        t() // 以函数形式调用时,this指向的是window
        // 当前箭头函数声明在对象的sayHello方法中
        // 当前作用域的this为调用该方法的对象
        const t2 = () => {
            console.log("t2 -->", this)
        }
        t2()
    }
}
obj.fn() // obj
obj.fn2() // window
obj.sayHello()

🥽 严格模式

  • JS运行代码的模式有两种:
  • 正常模式
  • 默认情况下代码都运行在正常模式中,在正常模式,语法检查并不严格
  • 它的原则是:能不报错的地方尽量不报错
  • 这种处理方式导致代码的运行性能较差
  • 严格模式
  • 在严格模式下,语法检查变得严格
  1. 禁止一些语法
  2. 更容易报错,有些正常模式下不报错的在严格模式下会报错,如a = 10即直接给变量a赋值没有声明,在正常模式下不报错,但是在严格模式下会报错
  3. 提升了性能
  • 在开发中,应该尽量使用严格模式,这样可以将一些隐藏的问题消灭在萌芽阶段,同时也能提升代码的运行性能
  • 开启严格模式
  • 全局严格模式,在js代码的开头(js代码第一行),写如下代码开启全局严格模式,"use strict"
"use strict" // 全局的严格模式
a = 10
  • 只在函数中开启严格模式
function fn(){
    "use strict" // 函数的严格的模式
    ...
}

🥽 面向对象

🌊 面向对象概述

  • 面向对象编程(OOP)
  1. 程序是干嘛的?
  • 程序就是对现实世界的抽象
  1. 对象是干嘛的?
  • 一个事物抽象到程序中后就变成了对象
  • 在程序的世界中,一切皆对象
  1. 面向对象的编程
  • 面向对象的编程指,程序中的所有操作都是通过对象来完成
  • 做任何事情之前都需要先找到它的对象,然后通过对象来完成各种操作
  • 一个事物通常由两部分组成:数据和功能
  • 一个对象由两部分组成:属性和方法
  • 事物的数据到了对象中,体现为属性
  • 事物的功能到了对象中,体现为方法
const five = {
     // 添加属性
     name:"王老五",
     age:48,
     height:180,
     weight:100,
     // 添加方法
     sleep(){
         console.log(this.name + "睡觉了~")
     },
     eat(){
         console.log(this.name + "吃饭了~")
     }
}

🌊 类

  • 使用Object或对象字面量{}创建对象的问题:
  1. 无法区分出不同类型的对象
  2. 不方便批量创建对象
  • 在JS中可以通过类(class)来解决这个问题:
  1. 类是对象模板,可以将对象中的属性和方法直接定义在类中,定义后,就可以直接通过类来创建对象。类好比汽车制作的图纸,每个汽车为汽车这个类对应的汽车对象。
  2. 通过同一个类创建的对象,我们称为同类对象,可以使用instanceof来检查一个对象是否是由某个类创建,如果某个对象是由某个类所创建,则我们称该对象是这个类的实例。
  • 类是创建对象的模板,要创建对象第一件事就是定义类,定义类的语法:
class 类名 {} // 类名要使用大驼峰命名
const 类名 = class {}
  • 通过类创建对象
new 类()
// Person类专门用来创建人的对象
class Person{
}
// Dog类式专门用来创建狗的对象
class Dog{
}
// 使用类创建对象,便于批量创建对象
const p1 = new Person()  // 调用类的构造函数创建对象
const p2 = new Person()
console.log(p1, p2) // 使用类创建对象,可以区分出不同类型的对象
const d1 = new Dog()
const d2 = new Dog()
console.log(d1, d2)
console.log(p1 instanceof Person) // true
console.log(d1 instanceof Person) // false

🌊 属性

类构造出来的对象,传统的属性添加方法

class Person {}
const p1 = new Person()
p1.name = 'Tom'
console.log(p1)

类的代码块,默认就是严格模式,类的代码块是用来设置对象的属性和方法的,不是什么代码都能写

class Person {
    // Person的实例属性
    // 在类中定义了类的实例属性,通过该类创建出来的对象都会具有一个属于自己的该属性
    // 实例属性只能通过实例访问
    // 调用实例属性 对象名.实例属性
    name = "孙悟空" // Person的实例属性name,并附初始值,也可以不赋初始值,则默认初始值为undefined
    age = 18
    // 使用static声明的属性,是静态属性(类属性)
    // 静态属性只能通过类去访问
    // 调用静态属性 类名.静态属性
    static hh = "静态属性"
}
const p1 = new Person()
p1.name = 'Tom' // 访问p1的实例属性name
console.log(p1)
console.log(Person.hh) // 访问Person的类属性

🌊 方法

class Person {
    // 实例属性
    name = "Tom"
    // 方法
    // 方法其实也是属性,只是方法的值为函数
    // 添加方法的方式一
    sayHi = function() { // 实例方法
        console.log('hi world')
    }
    // 添加方法的方式二
    // 添加方法可以直接写 `方法名+参数列表+方法体`
    sayHello() {  // 实例方法
        console.log('hello world')
    }
    // 实例方法的调用 对象.方法()
    // 实例方法中的this为调用方法的实例对象
    printThis() {
        console.log('实例方法的this: ', this)
    }
    // 静态方法(类方法) 
    // 通过类来调用 类.方法()
    // 静态方法中this指向的是当前类
    static printStaticThis() {
        console.log('静态方法的this: ', this)
    }
}
const p1 = new Person()
console.log(p1)
p1.sayHi()
p1.sayHello()
p1.printThis()
Person.printStaticThis()

🌊 构造函数

在类中可以添加一个特殊的方法constructor,该方法我们称为构造函数(构造方法),构造函数会在我们调用类创建对象时执行,我们可以在构造函数中,为实例属性进行赋值。

在构造函数中,this表示当前所创建的对象。

class Person {
    constructor(name, age, gender) {
      console.log("构造函数执行了~", name, age, gender)
        // this指向当前创建出来的对象
        // 向创建出来的对象中添加属性name,其值为通过参数传递过来的name
        // 对象的属性可以动态添加
        this.name = name 
        this.age = age
        this.gender = gender
    }
}
const p1 = new Person('孙悟空', 18, '男')
const p2 = new Person('猪八戒', 28, '男')
const p3 = new Person('沙和尚', 38, '男')
console.log(p1)
console.log(p2)
console.log(p3)

🌊 面向对象的三大特性

  • 面向对象的三大特性:
  • 封装 —— 安全性
  • 继承 —— 扩展性
  • 多态 —— 灵活性

💦 封装

  • 对象就是一个用来存储不同属性的容器
  • 对象不仅存储属性,还要负责数据的安全,即还要保证数据的合法性
  • 直接添加到对象中的属性,并不安全,因为它们可以被任意的修改
  • 如何确保数据的安全:
  1. 私有化数据
  • 将需要保护的数据设置为私有,只能在类内部使用
  • 实例属性使用#开头就变成了私有属性,私有属性只能在类内部访问
  1. 提供setter和getter方法来开放对数据的操作
  • getter方法,用来读取属性
  • setter方法,用来设置属性
  • 属性设置私有,通过getter setter方法操作属性带来的好处
  1. 可以控制属性的读写权限
  2. 可以在方法中对属性的值进行验证
  • 封装主要用来保证数据的安全
  • 实现封装的方式:
  1. 属性私有化加#
  2. 通过getter和setter方法来操作属性
// getter与setter使用下面的写法
// 调用属性和修改属性可以使用 对象.属性
// 但是在调用属性和修改属性时会自动调用相应的getter和setter方法
get 属性名(){
  return this.#属性
}
set 属性名(参数){
  this.#属性 = 参数
}
class Person {
    // 实例使用#开头就变成了私有属性,私有属性只能在类内部访问
    #name
    #age
    #gender
    constructor(name, age, gender) {
        this.#name = name
        this.#age = age
        this.#gender = gender
    }
    sayHello() {
        console.log(this.#name)
    }
    // getter方法,用来读取属性
    getName(){
        return this.#name
    }
    // setter方法,用来设置属性
    setName(name){
        this.#name = name
    }
    getAge(){
        return this.#age
    }
    setAge(age){
        // 校验数据是否合法
        if(age >= 0){
            this.#age = age
        }
    }
    // getter与setter使用下面的写法
    // 调用属性和修改属性可以使用 对象.属性
    // 但是在调用属性和修改属性时会自动调用相应的getter和setter方法
    get gender(){
        return this.#gender
    }
    set gender(gender){
        this.#gender = gender
    }
}
const p1 = new Person("孙悟空", 18, "男")
console.log(p1.getName())
// -11 修改的数据不合法,在setter方法中判断不合法不会继续修改,保证了数据的合法
p1.setAge(-11) 
p1.gender = "女" // getter与setter使用 `get 属性` `set 属性` 写法的调用
console.log(p1.gender)
console.log(p1)

💦 多态

多态,在JS中不会检查参数的类型,所以这就意味着任何数据都可以作为参数传递,要调用某个函数,无需指定的类型,只要对象满足某些条件即可。即调用某个函数不关心函数参数的类型,关心函数的参数是否具有某些特点。

多态为我们提供了灵活性。

class Person{
    constructor(name){
        this.name = name
    }
}
class Dog{
    constructor(name){
        this.name = name
    }
}
class Test{
}
const dog = new Dog('旺财')
const person = new Person("孙悟空")
const test = new Test()
function sayHello(obj){
    if(obj.hasOwnProperty('name')){ // 判断对象是否具有name属性
        console.log("Hello,"+obj.name)
    }
}
sayHello(dog)
sayHello(person)
sayHello(test)

💦 继承

  • 可以通过extends关键来完成继承
  • 当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)
class Animal{
    constructor(name){
        this.name = name
    }
    sayHello(){
        console.log("动物在叫~")
    }
}
class Dog extends Animal{
}
class Cat extends Animal{
}
const dog = new Dog("旺财")
const cat = new Cat("汤姆")
dog.sayHello()
cat.sayHello()
  • 继承发生时,被继承的类称为 父类(超类),继承的类称为 子类
  • 通过继承可以减少重复的代码,并且可以在不修改一个类的前提对其进行扩展
  • 在子类中,可以通过创建同名方法来重写父类的方法
class Dog extends Animal{
    // 在子类中,可以通过创建同名方法来重写父类的方法
    sayHello(){
        console.log("汪汪汪")
    }      
}
const dog = new Dog("旺财")
const cat = new Cat("汤姆")
dog.sayHello()
cat.sayHello()
  • 重写构造函数时,构造函数的第一行代码必须为super(),super表示父类,super()调用父类的构造函数
class Cat extends Animal{
    // 重写构造函数
    constructor(name, age){
        // 重写构造函数时,构造函数的第一行代码必须为super()
        super(name) // 调用父类的构造函数
        this.age = age
    }
    sayHello(){
        // 调用一下父类的sayHello
        super.sayHello() // 在方法中可以使用super来引用父类的方法
        console.log("喵喵喵")
    }
}
const dog = new Dog("旺财")
const cat = new Cat("汤姆", 3)
dog.sayHello()
cat.sayHello()
console.log(dog)
console.log(cat)
  • 通过继承可以在不修改一个类的情况下对其进行扩展
  • OCP 开闭原则,程序应该对修改关闭,对扩展开放

🌊 对象的结构

  • 对象中存储属性的区域实际有两个:
  1. 对象自身
  • 直接通过对象所添加的属性,位于对象自身中
  • 在类中通过 x = y 的形式添加的属性,位于对象自身中
class Person {
    name = "孙悟空" // 在类中通过 x = y 的形式添加的属性
    age = 18 // 在类中通过 x = y 的形式添加的属性
    constructor(){
        this.gender = "男" // 通过对象所添加的属性
    }
}
const p = new Person()
p.address = "花果山" // 通过对象所添加的属性
  1. 原型对象(prototype)
  • 对象中还有一些内容,会存储到其他的对象里(原型对象)
  • 在对象中会有一个属性用来存储原型对象,这个属性叫做__proto__
  • 原型对象也负责为对象存储属性,当我们访问对象中的属性时,会优先访问对象自身的属性,对象自身不包含该属性时,才会去原型对象中寻找
  • 会添加到原型对象中的情况:
  1. 在类中通过xxx(){}方式添加的方法,位于原型中
  2. 主动向原型中添加的属性或方法

🌊 原型对象

💦 访问一个对象的原型对象

// 方式一
对象.__proto__ // 不推荐
// 方式二
Object.getPrototypeOf(对象)
class Person {
    name = "孙悟空"
    age = 18
    sayHello() {
        console.log("Hello,我是", this.name)
    }
}
const p = new Person()
console.log(p)
// 一般以下划线开头的属性都是不希望直接进行访问的属性
console.log(p.__proto__)

const p = new Person()
console.log(p)
// 一般以下划线开头的属性都是不希望直接进行访问的属性
console.log(p.__proto__) 
console.log(Object.getPrototypeOf(p)) // 获取某个对象的原型对象

由结果可以看出,原型对象中的数据包含:1. 对象中的数据(属性、方法等)2. constructor (对象的构造函数,其实就是对象对应的类)

console.log(p.__proto__.constructor)
console.log(p.constructor)

相关文章
|
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