Day2 学习笔记

防抖

防抖是指当一个事件触发后,延迟一段时间执行处理函数。如果在延迟时间内再次触发该事件,则会重新计时延迟执行。

事件在n秒内多次触发,只执行最后一次

该防抖函数接受两个参数:

  • func:要进行防抖处理的函数。

  • wait:防抖的延迟时间,即事件触发后延迟多少毫秒执行函数。

防抖函数返回一个新的函数,该函数会在延迟时间内被连续调用时,清除之前的定时器,并重新设置新的定时器,直到延迟时间内没有新的调用。然后,函数 func 会被调用,并传递之前最后一次调用的参数。

// 定义防抖函数
function debounce (func, wait) {
            // timer定时器
            var timer = null
            return function () {
                if (timer !== null) {
                    clearTimeout(timer)
                }
                timer = setTimeout(func, wait)
            }
        }
// 模拟搜索操作,这里输出搜索内容
function search() {
axios.get('http://www.winweb.cloud/mall/demo/checkUser?name=' + userName.value).then(function (res) {
                // console.log(res)
                var result = res.data
                if (result.code !== 10000) {
                    message.innerHTML = result.message
                }
            })
}
​
// 监听input框输入事件
userName.addEventListener('input',function () {
            var str = checkUserName(userName.value)
            if (str === '该用户名合法') {
                message.innerHTML = ''
                debounceFn()
            } else {
                message.innerHTML = str
            }
        })

节流

节流是指在一段时间内限制事件的触发次数。当事件触发后,会立即执行处理函数,并在指定的时间间隔内,忽略后续的触发事件。

事件被连续触发,函数在指定的时间段内只执行一次。

// 定义节流函数
function throttle(func, wait) {
  var timeout;
  
  return function() {
    if (!timeout) {
      // 第一次触发立即执行
      func();
      timeout = setTimeout(function() {
        timeout = null;
      }, wait);
    }
  }
}
​
// 模拟页面滚动事件处理函数
function handleScroll() {
  console.log("Handling scroll event...");
}
​
// 创建节流函数实例
var throttleScroll = throttle(handleScroll, 200);
​
// 监听页面滚动事件
window.addEventListener("scroll", throttleScroll);
  • 第一次执行,直接进入定时器,此时已经赋值给timerid,设定是100ms之后再执行定时器里的代码,如果100ms之内再次触发,则因为timerid被赋值不会再进入第一个循环,直接return,结束function,即不执行第二次的点击事件

防抖节流实战

使用第三方库

https://www.lodashjs.com/

在vue中使用

src/utils.js

/**
 * func 要进行防抖的函数
 * wait 防抖延时的时间
 * immediate 首次触发是否需要立即执行
 */
export function debounce (func, wait = 500, immediate = false) {
    let timer
    return function () {
        // let context = this
        // let arg = arguments
​
        if (immediate) {           
            func(arguments)
            immediate = false
            return
        }
​
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            func(arguments)
        }, wait)
    }
}
​
/**
 * func 要进行节流的函数
 * wait 节流延时的时间
 * immediate 首次触发是否需要立即执行
 */
​
export function throttle (func, wait = 500, immediate = false) {
    let timer
    return function () {
        if (immediate) {
            func(arguments)
            immediate = false
            return
        }
​
        if (timer) return
        timer = setTimeout(() => {
            func(arguments)
            clearTimeout(timer)
        }, wait)
    }
}

app,vue

<el-button type="primary" @click="debounceFn">立即创建</el-button>
<script>
import { debounce } from './utils'
export default {
  data() {
    const debounceFn = debounce(this.onSubmit, 1000, true)
    return {
      form: {
       。。。
      },
      debounceFn
    };
  },
  methods: {
    onSubmit() {
      console.log("submit!");
    },
  },
};
</script>

性能优化

Web性能优化是提高Web应用程序加载速度、响应性和用户体验的一系列技术和最佳实践。以下是一些常见的Web性能优化技术:

  1. 压缩和合并资源: 压缩CSS、JavaScript和HTML等前端资源,以减小文件大小并提高下载速度。同时,将多个文件合并为较少的文件可以减少请求次数。

  2. 使用缓存: 利用浏览器缓存机制来存储经常使用的资源,例如静态文件(图片、CSS、JavaScript等)。合理设置缓存头信息,使得浏览器可以缓存这些文件并在后续访问时直接使用缓存而不是重新下载。

  3. 延迟加载: 对于页面中非必要的内容(例如图片、广告、社交媒体插件等),可以延迟加载,即在页面初次加载完成后再去加载它们,以提高初始化页面的速度。

  4. 优化图像: 使用适当的图像格式(如JPEG、PNG、WebP)和压缩算法,以减小图像文件的大小。同时,根据实际显示大小调整图像尺寸,避免在页面中显示超过需要的图像大小。

  5. 减少重绘和重排: 避免频繁的DOM操作和样式更改,因为它们可能引起浏览器的重绘(重新绘制页面元素外观)和重排(重新计算页面布局)。使用CSS3的硬件加速特性(如transformopacity)可以减少重绘和重排的开销。

  6. 异步加载和推迟脚本: 将JavaScript脚本标记为异步(async)或推迟(defer),以避免阻塞页面的解析和渲染。异步脚本会并行下载并在下载完成后立即执行,而推迟脚本会在文档解析完毕后再执行。

  7. 使用CDN加速: 使用内容分发网络(CDN)来分发静态资源,将这些资源缓存在全球各个服务器上,使得用户可以从最接近他们地理位置的服务器获取资源,从而加速加载速度。

  8. 优化网络请求: 减少http请求,合并多个请求,使用HTTP/2协议提高请求效率,使用资源预加载和懒加载等技术来优化页面加载过程。

  9. 优化服务器响应: 优化服务器端代码和数据库查询,减少响应时间。使用缓存、数据库索引和异步处理等技术来提高服务器的响应性能。

  10. 性能监测和分析: 使用工具和服务来监测和分析网站的性能指标,例如页面加载时间、渲染时间、资源大小等。根据性能分析结果,针对性地进行优化。

正则表达式

let var const

  1. 作用域:

    • var 声明的变量具有函数作用域,它在整个函数内部都是可见的。

    • letconst 声明的变量具有块级作用域,它们在声明的块(例如,if语句、循环等)内部可见,而在块之外是不可见的。

  2. 变量提升(Hoisting):

    • var 声明的变量会发生变量提升,即在函数或全局作用域中的任何位置声明的变量,都会被提升到作用域的顶部。但是,变量的赋值操作不会提升。

    • letconst 声明的变量也会发生变量提升,但与 var 不同,它们的变量在提升阶段不会被初始化(即不可访问),直到变量的声明语句被执行到。

  3. 重复声明:

    • var 允许在同一作用域内重复声明同一个变量,而后面的声明会覆盖前面的声明。

    • letconst 不允许在同一作用域内重复声明同一个变量,如果尝试重复声明会引发语法错误。

  4. 赋值和可变性:

    • varlet 声明的变量是可变的(Mutable),可以在后续代码中重新赋值。

    • const 声明的变量是不可变的(Immutable),一旦赋值后就不能再修改。

  5. 暂时性死区(Temporal Dead Zone):

    • letconst 声明的变量会在块级作用域中创建一个暂时性死区。即在变量声明之前的区域,变量是不可访问的,任何访问都会导致引用错误。

  6. 全局对象属性:

    • var 声明的全局变量会成为全局对象(浏览器环境中为 window)的属性。

    • letconst 声明的全局变量不会成为全局对象的属性。

暂时性死区

在代码块内,使用let命令声明变量之前,该变量都是不可用的。

if (true) {
  tmp = 'abc'; // ReferenceError  
let tmp;
}
​
不允许重复声明
// 全局的
    var x = 1;
    let x = 2;
// 报错
​
​
// 函数内部
​
function get(){
        var b =1;
        let b =6;
}
​
function get(b){  
       let b       // 报错,因为b已声明过了,不允许再let声明
}

块级作用域

  1. 为什么要有块级作用域? ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景

  • 第一种场景,内层变量可能会覆盖外层变量。

var tmp = new Date();
function f() {
console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }}
f(); // undefined ,在预编译的过程中。会先找var声明的变量,赋值为undefined
  • 第二种场景,用来计数的循环变量泄露为全局变量

var s = [1,2,3,4,5];
for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}
console.log(i); // 5

var i = 0;
for (; i < s.length;) {
  console.log(s[i]);
  i++
}
  1. 特點:允许块级作用域的任意嵌套

  • 每一层都是一个单独的作用域。每一層中的數據是不互通的

{
        {
            let a = 123;
        }
        console.log(a) // 報錯
}


// 裏面的可以訪問外賣的
{
        {
            let a = 123;
            {
                console.log(a)
            }
        }
        
    }

  1. 对比es5之前的代码块

ES6 方便 
let a = '全局';
    {
        let a = '局部'
        console.log(a);   
    }
    console.log(a);
=========================================
es5写法    
    var aa = '全局';
    (function(){
        var aa = '局部'
        console.log(aa);
        
    })()
    console.log(aa);

const

特殊的地方const

  1. const声明一个只读的常量。一旦声明,常量的值就不能改变

const PI = 3.1415;
PI // 3.1415
PI = 3;
  1. const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值

const foo;
// SyntaxError: Missing initializer in const declaration

上面代码表示,对于const来说,只声明不赋值,就会报错。 本质 内存地址不变 里面的东西可以边

const foo = {};
// 为 foo 添加一个属性,可以成功foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

下面是另一个例子。

const a = [];
a.push('Hello'); // 可执行a.length = 0;    // 可执行
a = ['Dave'];    // 报错
// 上面代码中,常量a是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给a,就会报错。
// 如果真的想将对象冻结,应该使用Object.freeze方法。
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;// 严格模式时,该行会报错
foo.prop = 123;
'use strict';
const foo = Object.freeze({});
foo.prop = 123; // 報錯

上面代码中,常量foo指向一个冻结的对象,所以添加新属性不起作用,严格模式时还会报错。

案例 for循环的计数器,就很合适使用let命令。

for (let i = 0; i < 10; i++) {
  // ...}

console.log(i);
// ReferenceError: i is not defined

上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。 下面的代码如果使用var,最后输出的是10。

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };}
a[6](); // 10

上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。 如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };}
a[6](); // 6

上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。 另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc// abc// abc

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。

数组的结构赋值

// 基本用法
var [a, b, c] = [1, 2, 3];
console.log(a); // 输出: 1
console.log(b); // 输出: 2
console.log(c); // 输出: 3

// 模式匹配,只要等号两边的模式相同,左边的变量就会被赋予对应的值
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

//
let [x, y, ...z] = ['a'];
x // "a"
y // undefined,因为目标数组只有一个元素而解构模式中的第二个变量没有匹配的值。因此,y 的值为 undefined。
z // []

// 忽略某些元素
var [x, , y] = [4, 5, 6];
console.log(x); // 输出: 4
console.log(y); // 输出: 6

// 
var fruits = ["apple", "banana", "orange"];
fruits.forEach((value) => console.log(value)); // apple  banana  orange

var fruits = [
    ['apple','red'], 
    ['banana','yellow'],
];
fruits.forEach(([key,value]) => console.log([key,value]))
//apple red
// banana yellow
// 剩余元素
var [m, n, ...rest] = [7, 8, 9, 10];
console.log(m); // 输出: 7
console.log(n); // 输出: 8
console.log(rest); // 输出: [9, 10]  `...` 来获取剩余的元素然后生成一个数组
```

数组解构赋值通过方括号 `[]` 来匹配数组中的元素,并将它们按照顺序赋值给对应的变量。我们可以使用逗号 `,` 来忽略某些元素,并使用剩余元素语法 `...` 来获取剩余的元素。

对象的解构赋值

对象解构赋值允许你使用对象字面量的语法将对象的属性赋给各种变量。

作用: 解构赋值可以方便地将一组参数与变量名对应起来。 (可以很方便的提取我们对象中的值)

// 基本用法,name和age换换顺序输出不变,因为会跟据名字进行匹配
var { name, age } = { name: "John", age: 30 };
console.log(name); // 输出: "John"
console.log(age); // 输出: 30

// 别名
var { name: fullName, age: years } = { name: "John", age: 30 };
console.log(fullName); // 输出: "John"
console.log(years); // 输出: 30

// 默认值
var { city = "Unknown" } = { name: "John", age: 30 };
console.log(city); // 输出: "Unknown"

// 对象解构赋值通过花括号 `{}` 来匹配对象的属性,并将它们赋值给对应的变量。我们可以使用冒号 `:` 来为变量指定别名,并可以为属性设置默认值。

let { foo:baz } = {foo:'aaa',bar:'bbb'};
console。log(baz) // ’aaa‘
根据解构赋值的规则,将会根据属性名匹配将目标对象的属性值赋值给对应的变量。在这种情况下:
{foo: baz} 中的 foo 是模式匹配的属性名,表示将目标对象的 foo 属性值赋值给变量 baz。
因为目标对象有一个属性 foo,因此 baz 变量会接收到属性 foo 的值 'aaa'。
console。log(foo) // ’undefined‘
其中 foo 是一个属性名,而 baz 是变量名,但是我们没有定义变量 foo。
注意:与数组一样,解构也可以用于嵌套结构的对象。
let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
对象的解构赋值可以取到继承的属性。
const obj1 = {};
const obj2 = { foo: 'bar' };
// Object.setPrototypeOf(obj1, obj2);
// obj1.__proto__ = obj2
const { foo } = obj1;
console.log(foo);
指定默认值

对象的解构也可以指定默认值。**

var {x = 3} = {};x // 3
var {x, y = 5} = {x: 1};x // 1y // 5
var {x: y = 3} = {};y // 3
var {x: y = 3} = {x: 5};y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
// 默认值生效的条件是,对象的属性值严格等于undefined。
var {x = 3} = {x: undefined};x // 3
var {x = 3} = {x: null};
x // null
// 上面代码中,属性x等于null,因为null与undefined不严格相等,所以是个有效的赋值,导致默认值3不会生效。
提取json数据
// 使用对象解构赋值从 jsonData 对象中提取属性值并将其赋值给对应的变量。
let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42 "OK" [867, 5309]

Symbol

ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名

ES6 数据类型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还新增了 Symbol 。

  1. 唯一性: 每个使用 Symbol() 构造函数创建的 Symbol 值都是唯一的,不会与其他 Symbol 值相等,即使它们的描述符相同。

  2. 不可变性: Symbol 值是不可变的,一旦创建就不能修改。这意味着它们不具备类似字符串或数字的方法(如修改值、拼接等)。

  3. 作为属性键: Symbol 值可以用作对象的属性键(属性名)。由于其唯一性,使用 Symbol 作为属性键可以避免命名冲突的风险。

  4. 隐藏性: 通过 Symbol 创建的属性键是隐藏的,意味着它们不会出现在常规的对象遍历操作(例如 for...in 循环)中。这可以用于定义一些特殊用途的属性,不会被意外访问或修改。

  5. Symbol注册表: JavaScript 提供了一个全局的 Symbol 注册表,可以在注册表中注册 Symbol,并通过相同的 Symbol 描述符获取相同的 Symbol 值。这对于确保在不同的模块中共享相同的 Symbol 值非常有用。

基本用法

Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。

let sy = Symbol("KK");
console.log(sy);   // Symbol(KK)
typeof(sy);        // "symbol"
 
// 相同参数 Symbol() 返回的值不相等
let sy1 = Symbol("kk"); 
sy === sy1;       // false

使用场景

作为属性名

用法

由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。用于定义对象的非字符串属性名

let sy = Symbol("key1");
 
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法2
let syObject = {
  [sy]: "kk"
};
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject);   // {Symbol(key1): "kk"}

Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。

let syObject = {};
syObject[sy] = "kk";
 
syObject[sy];  // "kk"
syObject.sy;   // undefined
symbol作为属性时是不可枚举
const Obj = {
	a:1,
	b:2
};
const s = Symbol()
obj[s]=3
console.log(Obj);
 
for (let i in Obj) {
  console.log(i);
}    // 无输出
 
Object.keys(Obj);                     // 不能输出sybol属性,['a','b']
// 可以通过 Object.getOwnPropertySymbols()返回symbol属性
Object.getOwnPropertySymbols(Obj);    // [Symbol()]
Reflect.ownKeys(Obj);                 // [Symbol(key1)]
==================================================
var obj = {
  prop1: "value1",
  prop2: "value2",
  [Symbol("hidden")]: "hiddenValue"
};

for (var key in obj) {
  console.log(key); // 输出: prop1, prop2
}

console.log(Object.getOwnPropertySymbols(obj)); // 输出: [Symbol(hidden)]
Symbol.iterator

通过实现 Symbol.iterator 方法,我们可以将对象转化为可迭代的对象,从而使其能够直接在 for...of 循环中使用

arr[Symbol.iterator] 表达式用于获取数组 arr 的迭代器对象。迭代器对象是一个具有 next() 方法的对象,用于按照特定顺序遍历数组的元素。

在 JavaScript 中,数组是可迭代的对象,因此它们默认实现了 Symbol.iterator 方法,返回一个迭代器对象。通过访问 arr[Symbol.iterator],我们可以获取到数组的默认迭代器。

以下是一个示例,展示了如何获取数组的迭代器对象并使用它来遍历数组的元素:
javascript

var arr = [1, 2, 3];

var iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // 输出: { value: 1, done: false }
console.log(iterator.next()); // 输出: { value: 2, done: false }
console.log(iterator.next()); // 输出: { value: 3, done: false }
console.log(iterator.next()); // 输出: { value: undefined, done: true }

在上述示例中,我们首先通过 arr[Symbol.iterator]() 获取数组 arr 的迭代器对象,并将其赋值给变量 iterator。然后,我们使用迭代器对象的 next() 方法来依次访问数组的元素。

每次调用 iterator.next() 方法都会返回一个包含 value 和 done 两个属性的对象,表示迭代器的当前状态。value 属性表示当前迭代位置的值,而 done 属性表示是否遍历完成。

通过获取数组的迭代器对象,我们可以手动控制数组元素的遍历过程,而不是依赖于 for...of 循环或其他迭代器相关的功能。

正则表达式

正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),可以用来描述和匹配字符串的特定模式。

正则表达式的模式

正则表达式的模式可以包括以下内容:

  • 字面值字符:例如字母、数字、空格等,可以直接匹配它们自身。

  • 特殊字符:例如点号 .、星号 *、加号 +、问号 ? 等,它们具有特殊的含义和功能。

  • 字符类:用方括号 [ ] 包围的字符集合,用于匹配方括号内的任意一个字符。

  • 元字符:例如 \d\w\s 等,用于匹配特定类型的字符,如数字、字母、空白字符等。

  • 量词:例如 {n}{n,}{n,m} 等,用于指定匹配的次数或范围。

  • 边界符号:例如 ^$\b\B 等,用于匹配字符串的开头、结尾或单词边界位置。

\d匹配0-9之间的任一数字,相当于[0-9]
\D匹配所有0-9以外的任一数字,相当于0-9
\w匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
\W除所有字母、数字和下划线以外的字符,相当于A-Za-z0-9_
\s匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f]
\S匹配非空格的字符,相当于\t\r\n\v\f
var rg = /\d/
rg.test('1') // true

var rg = /\S/
rg.test('S') // true

边界符号

var rg = /^123/ //匹配包含123开头的内容
console.log(rg.test('123')) // 1
console.log(rg.test('1234')) // 1
console.log(rg.test('5123')) // 0

var rg = /^123$/ //精准匹配,要求内容必须是123
^and$一起使用表示精确匹配.

字符集合

字符集合表示有一系列字符可供选择,只要匹配其中一个就可以。所有可供选择的字符都在方括号内

// 不带边界符
var rg = /[abc]/
rg.test('a')   1
rg.test('ab')   1
rg.test('abc')   1
rg.test('abcd')   1
// 加上起止边界符,只能匹配其中abc中的一个
var rg1 = /^[abc]$/
rg.test('a')   1
rg.test('b')   1
rg.test('c')   1
rg.test('ab')   0
rg.test('abc')   0
rg.test('abcd')   0
// 其他场景,只能取到一个值
var rg2 = /[a-z]/ // -表示取某个范围值的任意一个值
var rg3 = /[^a-z]/ // ^表示取反

量词符

量词符用来设定某个模式出现的次数

量词符说明
*重复零次或更多次
+重复一次或更多次
?重复零次或一次,只能是0次或者1次
{n}重复n次,只能匹配n次
{n, m}重复n到m次 ,可以是n次—m次
var rg = /^[a-z]0*$/  // 只能匹配a-z中的一个,0可以重复多次
rg.test('a0')  1 
rg.test('a01') 1不行。false
rg.test('a00') 1
rg.test('az00') z不行
var rg1 = /^[a-z]0+$/
var rg2 = /^[a-z]0?$/
var rg3 = /^[a-z]0{2}$/
var rg4 = /^[a-z]0{1,2}$/

分组符号

对字符进行分组,小括号包裹的内容可以看作一个整体

var rg = /^abc{1,3}$/ // 以ab开头,c结尾且c可以重复1到3次,此时的ab和c是分开的
var rg1 = /^(abc){1,3}$/ // abc成了一个整体

正则表达式的参数

思考一个问题 我们需要匹配到某个字符串中所有满足条件的字符,应该如何匹配?

正则表达式参数

/表达式/[switch]

表示说明
g全局搜索
i不区分大小写搜索
m多行搜索
s允许.匹配换行符
u使用unicode码的模式进行匹配
y执行“粘性(sticky)”搜索,匹配从目标字符串的当前位置开始
var str = ababab
var rg = /ab/g
console.log(str.match(rg)) // (2),{a,a}
组合使用,不区分大小写
var str = Aababab
var rg = /ab/gi
console.log(str.match(rg)) // (3),{A,a,a}

正则表达式常见的用法

replace替换

replace方法可以实现替换字符串操作

// str.replace(regexp|substr, newSubStr|function)
// 第一个参数可以是正则或者字符串
// 第二个参数为需要替换的字符串或者一个函数

// 要求1:将手机号的中间四位替换成*,例如13877283312变成138****3312
1 分组 前三位一组,后四位一组
/(\d{3}) \d{4} (\d{4})/
2 拼接
var str = '13877283312'
str.replace(/(\d{3})\d{4}(\d{4})/,'$1****$2')
$1****$2 // 第一个分组+****+第二个分组
    
// 要求2:单词首字母转为大写,例如my name is allen, i like code.
匹配首字母,然后转换为大写

习题

什么是事件委托

事件委托是一种常用的JavaScript技术,用于管理和处理多个子元素的事件。通过将事件处理程序绑定到它们共同的父元素上,从而避免为每个子元素都绑定事件处理程序。

instanceof 为什么可以区分数组和对象,比较的是什么?

当使用 instanceof进行比较时,比较的是对象的原型链,它会检查对象的原型链上是否存在指定构造函数的原型对象。如果存在,则返回 true,表示对象是该构造函数的实例;否则返回 false

const arr = [1, 2, 3];
const obj = { a: 1, b: 2 };

console.log(arr instanceof Array); // true,arr是数组的实例
console.log(obj instanceof Array); // false,obj不是数组的实例
console.log(obj instanceof Object); // true,obj是对象的实例

什么是防抖,节流 有哪些运用场景?

防抖:事件被触发,防抖会等待一段时间,直到事件触发停止,然后才执行事件处理函数。

使用场景:输入框搜索,窗口调整,按钮点击

节流:当事件触发后,会立即执行处理函数,并在指定的时间间隔内,忽略后续的触发事件。

使用场景:滚动事件,提交按钮

web性能优化能做哪些事情?
let const和var的区别?
  • 作用域:

    • var 声明的变量具有函数作用域,它在整个函数内部都是可见的。

    • letconst 声明的变量具有块级作用域,它们在声明的块(例如,if语句、循环等)内部可见,而在块之外是不可见的。

  • 变量提升(Hoisting):

    • var 声明的变量会发生变量提升,即在函数或全局作用域中的任何位置声明的变量,都会被提升到作用域的顶部。但是,变量的赋值操作不会提升。

    • letconst 声明的变量也会发生变量提升,但与 var 不同,它们的变量在提升阶段不会被初始化(即不可访问),直到变量的声明语句被执行到。

  • 重复声明:

    • var 允许在同一作用域内重复声明同一个变量,而后面的声明会覆盖前面的声明。

    • letconst 不允许在同一作用域内重复声明同一个变量,如果尝试重复声明会引发语法错误。

  • 赋值和可变性:

    • varlet 声明的变量是可变的,可以在后续代码中重新赋值。

    • const 声明的变量是不可变的,一旦赋值后就不能再修改。

  • 暂时性死区:

    • letconst 声明的变量会在块级作用域中创建一个暂时性死区。即在变量声明之前的区域,变量是不可访问的,任何访问都会导致引用错误。

  • 全局对象属性:

    • var 声明的全局变量会成为全局对象(浏览器环境中为 window)的属性。

    • letconst 声明的全局变量不会成为全局对象的属性。

Symbol的数据类型及特性?

唯一性:每个使用 Symbol() 构造函数创建的 Symbol 值都是唯一的,不会与其他 Symbol 值相等。

不可变性: Symbol 值是不可变的,一旦创建就不能修改。

Symbol.iterator的作用?

通过实现 Symbol.iterator 方法,我们可以将对象转化为可迭代的对象,从而使其能够直接在 for...of 循环中使用

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值