[Js]常识三

作用域

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

GC

在这里插入图片描述

Js Gc 算法

  • 引用计数(已淘汰)
  • 标记清除
    在这里插入图片描述在这里插入图片描述

closure

  • 一句话:内层函数 + 外层函数的变量 = 闭包
  • 作用:私有化数据,or 私有化状态
    在这里插入图片描述

变量和函数提升

  • Js 祖传var变量,目前不会再用
  • 变量的提升:只提升声明,不提升赋值
  • 除了变量提升,函数也可以提升,但是函数赋值并不能提升(!!!)
fun()

// 这种方式能正常输出到console 【函数声明提升】
// function fun(){
//     console.log('done')
// }

// 这种方式能正常 不能 输出到console 【函数赋值不能提升】 
fun = function fun(){
    console.log('done')
}

函数参数

  • 形参、实参
  • 默认参数
  • arguments
//arguments 是特殊的参数,本质是个【伪数组】,它只存在于函数
function sum() {
    let sum = 0;
    for (let index = 0; index < arguments.length; index++) {
        const element = arguments[index];
        sum += element;
    }
    return sum;
}
console.log(sum(1, 2, 3))
  • 剩余参数
// 剩余参数,有点像java中的可变参数 【开发过程中还是多使用剩余参数,而不是 arguments】
function config(baseURL, ...other) {
  console.log(baseURL)
  console.log(other) //Array
}

config('www.a.com', 'get', 'post')
  • 展开运算符
// 展开运算符,可以展开数组;注意【展开数组,不会修改原数组】
const arr1 = [1, 2, 3]
// 比如数组最大值
console.log(Math.max(...arr1)) // (method) Math.max(...values: number[]): number
console.log(Math.min(...arr1))

// 合并数组
const arr2 = [4, 5, 6]
console.log(...arr1, ...arr2)

箭头函数

跟java的lamda类似,不过js箭头函数还可以直接生成对象

//【箭头函数生成对象时,注意外面包裹着括号; 同时记得 js对象的属性是不需要引号的】
const f = (name) => ({ name: name }) 
console.log(typeof (f)) //function
console.log(f('ht')) // {name: 'ht'} ,注意name没有引号,这是个对象,不是个json

箭头函数的this

在这里插入图片描述

const f1 = () => {
   //【虽然this指window,但window是从上下文,也就是<script> 沿用到的。箭头函数自身是没有this的】
   console.log(this) //window 
}
f1()

const obj = {
   name: 'ht',
   sayHi: function () {
       console.log(this) //this 指代当前对象obj
   }
}
obj.sayHi()

const obj2 = {
   name: 'ht2',
   sayHi: function () {
       console.log('------', this) //this 指代当前对象obj
       const hello = () => {
           console.log('++++++', this) // this 也 指代当前对象obj
       }

       hello()

       const hay = function () {
            // this 指代 window,hay()的执行本质是 window.hay(),注意这里不是箭头函数,而是function
           console.log('______', this)
       }
       hay()
   }
}
obj2.sayHi()

DOM中的箭头函数

<script>
    const butt = document.querySelector('button')
    // 箭头函数指向 window
    butt.addEventListener('click', () => {
        console.log('-------', this)
    });

    // 箭头函数指代DOM,也就是 butt 对象
    butt.addEventListener('click', function () {
        console.log('+++++++', this)
    })
</script>

#在这里插入图片描述

解构赋值

数组解构赋值

在这里插入图片描述
在这里插入图片描述

数组解构必须加分号

在这里插入图片描述

const [a, b, c] = [1, 2, 3]
console.log(a) // 能正常得到a 的赋值 ,所以这种语法就是个 语法糖

let x = 1
let y = 2; // 【这里必须要加分号,否则js会把下一行的 [x,y]当做是这一句的一部分,这可能也是Js是个蛋疼语言的原因】
[x, y] = [y, x] // 使用解构语法糖,将 x,y 两个变量进行了值的互换
console.log(x) //2 

解构语法中,参数和参数值不匹配的特殊情况

const [a, b] = [1]
console.log(a)
console.log(b) //undefined

const [c, d] = [1, 2, 3]
console.log(c)//1

// 解构支持可变参数
const [e, f, ...g] = [1, 2, 3, 4]
console.log(typeof (g)) //object 
console.log(g) //[3,4] ,伪数组

//解构语法也支持多维数组
const [h, [i, j]] = [5, [6, 7]]
console.log(i) //6

对象解构赋值

//解构对象 【其实也就是语法糖】
  const person = {
      name: 'John',
      family: {
          father: 'Leo',
          sister: 'lily'
      },
      age: 10
  }
  // const { name, family: { father, sister } } = person;
  // console.log(father)
  const { name, family: parent } = person;
  console.log(parent) // 【相当于获取了一个 family 变量的别名】

深入对象

// 创建对象的三种姿势
// 其一:对象字面量
const obj = { name: 'ht' }
// 其二:new Object()
const obj2 = new Object()
obj2.name = 'ht'
// 其三:构造函数,这在需要多次创建对象时十分有效
// 一般使用大写开头
function Human(name, age) {
    this.name = name // 实例成员
    this.age = age// 实例成员
}
const p1 = new Human('ht', 10)
const p2 = new Human('ht2', 20)

Human.eyes = 2 // 静态属性 【因为是操作了 Human这个构造函数】
Human.hi = function () {  // 静态方法
    return 'everyone says hello'
}

console.log(Human.hi())
console.log(p1)

js built-in functions

在这里插入图片描述

js内置构造函数 【注意叫做内置的 构造函数】。这和java的class类似了。

  • Object
  • Array
  • String
  • Number

Object

//--------------Object ----------------
// 构造函数Object内置静态方法
const obj = { name: 'ht', age: 10 }
// 获取所有键 或者 值的集合
console.log(Object.keys(obj))
console.log(Object.values(obj))
//遍历
for (const key in obj) {
   console.log(obj[key])
}
// 拷贝并增加属性
const copy = {}
Object.assign(copy, obj)
console.log(copy)

Object.assign(copy, obj, { gender: "male" })
console.log(copy)

Array

  • foreach
  • map
  • reduce
  • join
  • find
  • every
  • some
  • concat
  • sort
  • splice
  • reverse
  • findIndex
  • Array.from() 【静态方法】
const arr = Array.from([-2, -1, 1, 2])
console.log(typeof (arr)) //object
console.log(arr instanceof Array) //true
console.log(arr)

console.log(arr.join(';'))
console.log(arr.concat([3, 4]))
console.log(arr.reverse())
console.log(arr.splice(1, 1))
console.log(arr.some(function (value, index, array) {
   return value === 2
}))
console.log(arr.sort(function (a, b) {
   return 1
}))

string

  • length
  • split()
  • substring
  • startsWith
  • includes
  • toUpperCase
  • toLowerCase
  • indexOf
  • replace
  • match
  • endsWith
    -…

js oop

构造函数

在这里插入图片描述
在这里插入图片描述

js 原型

  • 原型
  • constructor
  • 原型继承
  • 原型链

原型

在这里插入图片描述

原型继承

// 在原型上增加Array的方法
Array.prototype.sum = function () {
    return this.reduce(function (acc, val) { return acc + val });
}

const arr = [1, 2, 3, 4, 5]
console.log(arr.sum())

Array.prototype.max = function () {
    return Math.max(...this) // Sprede syntax,扩展参数
}
console.log(arr.max())

constructor

在这里插入图片描述

p.s. 窃以为这是js的设计缺陷:给构造函数protoype赋值,反而把构造函数的信息丢掉了

function Star() {
}
// 这种写法很繁琐,所以干脆像后面的写法,将 prototype 赋值为一个对象
// Star.prototype.sing=function(){}
// Star.prototype.dance=function(){}

Star.prototype = { //原型对象
    //如果么有这个 constructor 属性,sing 和dance 没法找回定义它们的构造函数
    // 窃以为这是Js设计的缺陷.
    // constructor 就是为了重新指回这个原型对象的构造函数
    constructor: Star,
    sing: function () { },
    dance: function () { }
}

console.log(Star.prototype)

对象原型

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

总结就是一句话:实例对象的对象原型(__proto__) === 构造函数的原型对象(prototype)

  • 对象原型 -----> 实例对象的原型,对象原型是对象
  • 原型对象 --> 构造函数的原型,原型对象是构造函数
  • 无论对象原型(__proto__) 还是 构造函数的原型对象(prototype),都有constructor属性,指向实例对应的构造函数
function Star(){}

const a = new Star();
const b = new Star();

//不同实例的对象原型是相同的
console.log(a.__proto__ === b.__proto__); //true

//不同实例的对象原型不仅是相同的,而且等于构造函数的原型对象
console.log(a.__proto__ === Star.prototype);//true

// 实例的对象原型和构造函数的原型对象都有 指针指向构造函数
console.log(a.__proto__.constructor === Star.prototype.constructor)//true

console.log(a.__proto__) // {constructor.f}

原型继承

先看一个有缺陷的做法:

const Person = {
   eyes: 2
}
function Man() {
}
// Man 构造函数的原型对象是 Person ,【注意 Person不是构造函数,而是一个对象】
Man.prototype = Person;

const man = new Man()
//对象man继承到了 eyes 属性
console.log(man['eyes'])
console.log(Man.prototype) // 少了 constructor 属性

Man.prototype.constructor = Man; 
console.log(Man.prototype) // 加上constructor属性

function Woman(){}
Woman.prototype = Person;
Woman.prototype.haveBaby = function(){
   console.log('havebaby')
}

// 【虽然只是给Woman.prototype加了新方法,但是man对象也受到了影响,】
// 本质是因为: prototype 是个引用,真正的原型对象是 Person 
man.haveBaby() // havebaby 

再看一个改善的做法:

function Person() {
    this.eyes = 2
}
function Man() {}
function Woman() {}

// Man 构造函数的原型对象是 Person实例
Man.prototype = new Person();
Man.prototype.constructor = Man;

Woman.prototype = new Person()
Woman.prototype.constructor = Woman

Woman.prototype.haveBaby = function(){
    console.log("havebaby")
}
const man = new Man()
// caught TypeError: man.haveBaby is not a function
console.log(man.haveBaby()) 

原型链

总结下来有二句重点:

  • 只要是对象,就有__proto__
  • 只要是原型对象,就有constructor, 指向构造函数

在这里插入图片描述

原型链的本质是个查找规则
在这里插入图片描述

function Person() { }
const person = new Person()
// 下面几个引用构成了 原型链
console.log(person.__proto__) // 【对象 person 的 __proto__ 】
console.log(Person.prototype.__proto__); //  【person 对象原型的 __proto__ 】
console.log(Person.prototype.__proto__ === Object.prototype);  //true
console.log(Object.prototype.__proto__ === null);  // true  【object的__proto__】

console.log(Person.prototype.constructor); //ƒ Person(){}
console.log(Object.prototype.constructor); //ƒ Object() { [native code] }

原型小案例-- Modal

在这里插入图片描述

深浅拷贝

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

const obj = {
    uname: 'ht',
    family: {
        father: 'old ht'
    },
    greet: function () {
        console.log('hello');
    }
}
obj.greet(); //hello

const copy = JSON.parse(JSON.stringify(obj))
 // caught TypeError: copy.greet is not a function
 // 【说明: JSON 这种深拷贝的方式,只能copy 属性,不能copy方法】
copy.greet()
console.log(copy)

异常处理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

this

普通函数this

// 普通函数的this 指向依照原则: 谁调用我,我就指向谁
function hi(){
    console.log(this)
}

const hello = function(){
    console.log(this)
}

hi();//this 指向 window
hello() //this 指向 window

const ht = {
    name: 'ht',
    walk:function(){
        console.log('walk');
    }
}
ht.hi = hi
ht.hello = hello

ht.hi() // this 指代 ht 对象
ht.hello() // this 指代 ht 对象

箭头函数的this

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

改变this指向

call (有点类似java的反射)

在这里插入图片描述

apply

在这里插入图片描述

bind

在这里插入图片描述

小结

在这里插入图片描述

//1、 call() 方法。有点类似java的反射
const ht = {
    name: 'ht',
    hi: function (x, y) {
        console.log(x + y);
        console.log(this)
    }
}

ht.hi.call(ht, 1, 2) 

// 2. apply() ,注意传参有一个数组,这点和 call() 不一样
ht.hi.apply(ht, [1,3])

// apply()另有个典型作用
console.log(Math.max.apply(null, [1,2,3]))
const arr = [1,2,3];
console.log(Math.max(...arr))
console.log(Math.max([1,2,3]))

// 3.bind()
// - bind() “蓄而不发”,也就是 绑定了函数但是不执行
// - bind() 返回值是个函数
// - bind() 能改变this的指向【主要用途】
const func = ht.hi.bind(ht,1,2)
func()

使用案例:

const butt = document.querySelector('button')
butt.addEventListener('click', function (e) {
    console.log(this)
    this.disabled = true;
    /**
    setTimeout(function () {
        butt.disabled = false;// 不能使用 this,而要使用butt; 因为 this 指代的是 window
    }, 2000)
    */
    // 使用bind 强制将this指向 button,这样能起到相同的作用
    setTimeout(function () {
        this.disabled = false;
    }.bind(butt), 2000)
});

性能优化

防抖 debounce

在这里插入图片描述
在这里插入图片描述

<script src="./lodash-4.17.21/package/lodash.min.js">
</script>
<script>
   /* 为啥这里的代码写在上面的 script 标签里就会不起作用? */
   const box = document.querySelector('.box')
   let count = 0;
   const func = function (e) {
       box.innerHTML = count++;
       console.log(count);
   }
   // box.addEventListener('mousemove', func) 
   // 1)使用 lodash 的工具来实现
   // box.addEventListener('mousemove', _.debounce(func, 500))

   // 2) 自己实现
   let prev;
   box.addEventListener('mousemove', function (e) {
       if (prev) {
           clearTimeout(prev);
       }
       prev = setTimeout(func, 500)
   })
</script>

节流 throttle

在这里插入图片描述

  • lodash API 实现
    box.addEventListener('mousemove', _.throttle(func, 500))
  • 自己实现
function throttle(fun, t) {
    let prevTime = Date.now()
    // this is a closure 
    const f = function () {
        if (Date.now() - prevTime > t) {
            setTimeout(function () {
                fun()
            }, t)
            prevTime = Date.now()
        }
    }
    return f
}

box.addEventListener('mousemove', throttle(func, 500))

清除定时器的trap

<script>
   let timer = null;
   timer = setTimeout(function () {
       // 在setTimeout 中无法删除定时器,因为 定时器【还在运作】,所以使用timer=null
       // clearTimeout(timer);
       timer = null;
       console.log(timer)
   }, 1000)
</script>

小结

在这里插入图片描述

综合案例:如何保存视频的上一次观看的进度

在这里插入图片描述

  • 使用localStorage存取进度时间
  • video对象具有ontimeupdateonloadedata 方法
  • 使用lodash库的throttle() 实现“节流”(虽一直播放,但每秒才保存一次进度)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值