18day,19day-正则,es6函数相关,箭头函数,this

本文围绕ES6展开,介绍了正则表达式的创建、常用方法、元字符、标识符及特性;阐述了ES6函数相关内容,包括自执行函数、函数默认值和箭头函数特点;还深入讲解了this指向问题,涵盖几种情况、四个绑定规则及改变this指向的方法和规则优先级。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正则

认识正则

初识

正则表达式 又叫‘规则表达式’
由我们来书写规则,专门来检测字符串是否符合规则
最主要的应用场景—表单验证

如何创建正则

想要制定规则,必须按人家的要求的方式来制定
把一些字母和符号写在//中间的东西,叫做正则,比如/abcdefg/
创建正则分为两种方式 字面量构造函数

  1. 字面量
let reg = /abcdefg/
  1. 构造函数
let reg2 = new RegExp("abcdefg")
// 用构造函数和字面量创建的正则,得到的结果是一样的

常用方法

用来检测和捕获字符串中的内容

test

test() 检测
语法:正则表达式.test(“你要检测的字符串”);
返回值:一个布尔值
如果该字符串符合规则,那就是true,反之false

    let reg =  /abcd/;
    // 意义:字符串中必须包含 abcd 字符片段
    let res = reg.test("kasjgfkajsbkagjkjfgaksjfgkajf123");
    console.log(res);// false
    let res2 = reg.test("asdadbweriqhdqwhdoihec")
    console.log(res2); // false
    let res3 = reg.test("sjdkjadgabcdqwdhqweh123");
    console.log(res3);// true
exec

// 留着

元字符-基本字符

  1. 元字符
    所有的文本内容
    特殊符号,用符号表示一类内容

  2. 标识符
    书写在正则外面的,用来修饰正则表达式

基本元字符
  1. \d 表示一位数字
    let reg = /\d/;
    // 意义:字符串中至少包含了一位数字(0-9)
    let res = reg.test("asdj1123123123hvh!@#$%^()$");
    console.log(res); // true;
  1. \D表示一位 非数字
    let reg = /\D/;
    // 意义:字符串中至少包含了一位非数字内容
    let res = reg.test("123@12我456789");
    console.log(res); // true
  1. \s表示一位 空白内容(空格/缩进)
      let reg = /\s/;
      // 意义:字符串中至少包含了一位 空白内容
      let res = reg.test("ab cd");
      console.log(res); // true
      let res2 = reg.test("abcd");
      console.log(res2); // false
  1. \S表示一位 非空白内容
      let reg = /\S/;
      // 意义:字符串中至少包含一位 非空白内容
      console.log(reg.test("     1     ")); // true
      console.log(reg.test("         ")); // false
  1. \w表示一位 数字(0-9)字母(a-zA-Z)或下划线_中的任意一个
      let reg = /\w/;
      console.log(reg.test("~!@#$%^&**()"));// false
      console.log(reg.test("_"));true
      console.log(reg.test("B"));

      let reg2 = /ABC/;
      console.log(reg2.test("abcdefg"));
  1. \W表示一位 非数字字母下划线
    let reg = /\W/;
    console.log(reg.test("~!@#$%^&*()"));// true
    console.log(reg.test("_")); // false
  1. . 表示一位不是 \n 的字符
    let reg = /./;
    console.log(reg.test("\n\n\n\n\n"));//false
    console.log(reg.test("abcd\n"));//true

    // \n表示换行字符
    document.body.innerText = "你\n好\n啊\nZ\nD"
  1. \表示转义字符
  • 把有意义的符号转成没有意义的普通文本
    let reg = /\d\.\d/;
    console.log(reg.test("3.1")); // true
    console.log(reg.test("3a1"));// 一下都为false
    console.log(reg.test("321"));
    console.log(reg.test("3!1"));
    console.log(reg.test("3_1"));
    // 本身 . 是有特殊意义的 和\一起写
    // \就可以把 . 转成普通文本
  • 把没有意义的文本转成有意义的符号
    d 没有意义表示字符 d,但是加了\之后,\d表示一位数字

元字符–边界符号

  • ^:表示开头
  • $:表示结尾

注意:当^和$结尾一起使用的时候,表示的是从开头到结尾

^
    let reg = /\d/; // 表示至少一位数字
    let reg2 = /^\d/;// 表示字符串中必须以一个数字开头
    console.log(reg.test("abc1d"));// true
    console.log(reg2.test("abc1d")); // false
    console.log(reg2.test("1abc1d")); // true
$
    let reg = /\d/;// 表示至少一位数字
    let reg2 = /\d$/; // 表示字符串中以一个数字结尾
    console.log(reg.test("abd12d;")); // true
    console.log(reg2.test("1abcd"));// false
    console.log(reg2.test("1abcd2"));// true

小坑

    let reg = /^\d$/;
    // 表示:从开头到结尾只有一位数字
    console.log(reg.test("1a2")); // fasle
    console.log(reg.test("1")); // true
    console.log(reg.test("12")); // false
    console.log(reg.test("123"));// false
    console.log(reg.test("11"));// false

    let reg2 = /^\d.\d$/;
    // 表示:以一个数字开头,之后一个除换行以外的内容,再以一个数字结尾
    console.log(reg2.test("1a1")); // true
    console.log(reg2.test("11")); // false
    console.log(reg2.test("a1a")); // false
    console.log(reg2.test("a11")); // false
    console.log(reg2.test("11a")); // false
    console.log(reg2.test("1111")); // false
    console.log(reg2.test("111")); // true

元字符-限定符号

限定内容出现的次数

注意:一个限定符号只能修饰符号前面的一个内容的出现次数

*表示出现0~多次

前面的内容重复至少0次,也就是0~正无穷次

    let reg = /^\d*$/;
    // 表示字符串 从开头到结尾 数字出现0~无穷次
    // 空字符串
    // 只要有字符串 就必须开头到结尾都为数字
    console.log(reg.test("")); // true;
    console.log(reg.test("$")); // false
    console.log(reg.test("!")); // false
    console.log(reg.test("1")); // true
    console.log(reg.test("12")); // true
    console.log(reg.test("123")); // true
    console.log(reg.test("1234")); // true

    let reg2 = /\d*/;
    // 在字符串中数字可以出现0~∞
    console.log(reg2.test(""));// 都为true
    console.log(reg2.test("abc"));
    console.log(reg2.test("abc123"));
+ 表示出现1~多次
    let reg = /^\d+$/;
    // 表示字符串从开头到结尾必须为数字,且至少出现一次
    console.log(reg.test("")); // false
    console.log(reg.test("1")); // true
    console.log(reg.test("12")); // true
    console.log(reg.test("123")); // true
    console.log(reg.test("1234")); // true
    console.log(reg.test("1234a")); // true
    console.log(reg.test("12a34")); // true

    let reg2 = /\d+/;
    // 表示字符串中 数字至少出现一次
    console.log(reg2.test("abc"));// false
    console.log(reg2.test("abc1"));// true
    console.log(reg2.test("abc123"));// true
?表示出现 0~1次
    let reg = /^\d?$/;
    console.log(reg.test(""));// true
    console.log(reg.test("abc1"));// false
    console.log(reg.test("12"));// false 
    console.log(reg.test("123"));// false 
    console.log(reg.test("1234"));// false 
    console.log(reg.test("12345"));// false 

    let reg2 = /\d?/;
    // 表示字符串中 数字只能出现0次或1次
    console.log(reg2.test("")); // true
    console.log(reg2.test("abc1")); // true
    console.log(reg2.test("abc")); // true
    console.log(reg2.test("abc123")); // false
{n}表示指定出现n次
    let reg = /^\d{2}$/;
    // 表示字符串从开头到结尾,数字出现两次
    console.log(reg.test(""));// false
    console.log(reg.test("1"));// false
    console.log(reg.test("12"));// true
    console.log(reg.test("123"));// false

    let reg2 = /\d{3}/;
    // 表示字符串中 数字出先3次
    console.log(reg2.test("abc")); // false
    console.log(reg2.test("abc1"));// false
    console.log(reg2.test("abc123"));// true
{n,} 表示至少出现n次

=》{0,} 等价于*
=》{1,} 等价于+

    let reg = /^\d{2,}$/;
    // 表示字符串从开头到结尾,数字至少出现2次
    console.log(reg.test(""));
    console.log(reg.test("1"));
    console.log(reg.test("12"));
    console.log(reg.test("123"));
    console.log(reg.test("1234"));
    console.log(reg.test("12345"));

    let reg2 = /\d{2,}/;
    // 表示字符串中数字出现2~无穷次
    console.log(reg2.test("abc"));
    console.log(reg2.test("abc1"));
    console.log(reg2.test("abc12"));
    console.log(reg2.test("1abc12"));
{n,m} 出现 n~m次

=》{0,1} 等价于 ?

    let reg = /^\d{3,5}$/;
    //  表示字符串从开头到结尾 数字出现3~5次
    console.log(reg.test(""));
    console.log(reg.test("1"));
    console.log(reg.test("12"));
    console.log(reg.test("123"));
    console.log(reg.test("1234"));
    console.log(reg.test("12345"));
    console.log(reg.test("123456"));

    let reg2 = /\d{3,5}/
    // 表示字符串中,数字要出现3~5
    console.log(reg2.test("abc"));
    console.log(reg2.test("abc1"));
    console.log(reg2.test("abc123"));
    console.log(reg2.test("abc12345"));

小坑

    let reg = /^a{2}b{2}c{2}$/;
    console.log(reg.test("aabbcc")); // true
    console.log(reg.test("abcc")); // false
    console.log(reg.test("acbabc")); // false
    // 一个限定符号只能修饰符号前面的一个内容
    let reg2 = /a{2}b{2}c{2}/;
    console.log(reg2.test("aabbcc")); // true
    console.log(reg2.test("abcc")); // false
    console.log(reg2.test("acbabc")); // false
    // 一个限定符号只能修饰符号前面的一个内容

元字符–特殊符号

()
  1. 一个整体
// {2}修饰的是前面()内的全部内容
// abc要出现两次
    let reg = /^(abc){2}$/;
    console.log(reg.test("abc")); // false
    console.log(reg.test("abcabc")); // true

    let reg2 = /^abc{2}$/;
    console.log(reg2.test("abcabc")); // false
    console.log(reg2.test("abcc")); // true
    console.log(reg2.test("aabbcc")); // false
  1. 单独捕获
    留着
|

或者的意思
注意:或的边界,要么是(),要么是正则的边界

    let reg = /^a(b|c)d$/;
    // 表示字符串从开头到结尾 必须是 abd或者 acd
    // console.log(reg.test("abd")); // true
    // console.log(reg.test("acd")); // true
    // console.log(reg.test("abcd")); // false


    let reg2 = /^ab|cd$/;
    // => 以ab开头 ^abxxxxxx
    // 或
    // => 以cd结尾 xxxxcd$
    // console.log(reg2.test("abcd"));// t
    // console.log(reg2.test("ababc"));// t
    // console.log(reg2.test("1234cd")); // t
    // console.log(reg2.test("ab"));// t
    // console.log(reg2.test("cd")); // t

    let reg3 = /^(奥迪|宝马){2}$/;
    // 汽车可以是奥迪或宝马
    // {2}修饰的是(xxx) xxx 出现两次
    console.log(reg3.test("奥迪奥迪"));
    console.log(reg3.test("宝马宝马"));
    console.log(reg3.test("奥迪宝马"));
    console.log(reg3.test("宝马奥迪"));
    // 判断一个手机号
    // 以137、138、139开头,总共11位
    // 可以有+86也可以没有
    let reg = /^(\+86)?1(31|51|86)\d{8}$/;
    console.log(reg.test("+8613126172837")); // true
[]
  • 意义:包含
  • 注意:一个[]内可以写多个内容,但是一个[]只占一个字符的位置,表示[]内的任意一个内容都可以
    [0-9]等价于 \d
    [0-9a-zA-Z_]等价于 \w
    let reg = /^[abcd]$/;
    // 表示字符串从开头到结尾只有一个字符
    // 这一位字符可以是a、b、c、d
    // [abcd] 等价于 (a|b|c|d) [a-d]
    // console.log(reg.test("a"));
    // console.log(reg.test("b"));
    // console.log(reg.test("c"));
    // console.log(reg.test("d"));
    // console.log(reg.test("e"));
    // console.log(reg.test("ab"));

    let reg2 = /[abcd]/;
    // 表示该字符串中包含a|b|c|d其中一个即为true
    console.log(reg2.test("abcd"));
    console.log(reg2.test("ab"));
    console.log(reg2.test("abc"));
    console.log(reg2.test("a"));
    console.log(reg2.test("abcd123"));
[^]
  • 意义:非
  • 注意:一个[^]可以写多个内容,但是一个[]只占一个字符的位置,表示[]任意一个都不行
    [^0-9] 等价于\D
    [^0-9a-zA-Z_]等价于\W
    let reg = /^[^abcd]$/;
    // 表示字符串从开头到结尾只有一个字符
    // 且这个字符不能是abcd其中任意一个。其他都可以。
    console.log(reg.test("a"));
    console.log(reg.test("b"));
    console.log(reg.test("c"));
    console.log(reg.test("d"));
    console.log(reg.test(" "));// true
    console.log(reg.test("1"));// true
    console.log(reg.test("\n"));// true
    console.log(reg.test(" \n"));// false
-中划线
  • 意义:到 至
  • 需要和[]或者[^]连用
    let reg = /^[0-9]$/;
    // 意义:匹配0-9中的任意一个数字

标识符

书写在正则表达式的外面,专门用来修饰整个正则的符号

  1. i
  • 表示忽略大小写
    let reg = /abcd/;
    let reg2 = /abcd/i;
    console.log(reg.test("ABCD")); //  false
    console.log(reg2.test("ABCD"));// true
  1. g
    全局
    留着
exec

捕获
语法:reg.exec(字符串)
作用:从 字符串 中 把满足正则条件的部分取出来
返回值:

  1. 原始字符串中没有满足条件的字符串
  • null
  1. 原始字符串有满足条件的字符串
  • 正则没有()也没有全局标识符 g
    • 他的返回值是一个数组
      • 数组索引0项是捕获到的符合规则的字符串
      • index:表示第一个满足规则的字符串的开始下标
      • input:当前被检测的字符串
    • 注意:不管捕获多少次,每次都是从原始字符串的索引0开始检索
  • 有全局标识符 g
    • 返回值是一个数组
      • 数组索引0项是第一次捕获到的符合规则的字符串
      • index:表示满足规则的字符串的开始下标
      • input:当前被检测的字符串
    • 注意:第二次捕获是从第一次捕获的结束位置开始向后查询,知道最后捕获不到返回null,之后再下一次,又从0开始检索。
  • 有()

() 有两个意义

  • 一个整体
  • 单独捕获
    如果你只想使用第一个意义,整体所用,不想单独捕获,可以写成(?😃:表示不捕获
 // let reg = /\d{3}/;
  // let str = "askjdhakjsdhkasjdh";
  // let res = reg.exec(str);
  // console.log(res); // null

  // 有符合要求的片段
  // 1-1 没有()没有g
  // let str = "ajshdkajhd123ajshdkjahdajksdhk456kudhkajsdha";
  // let reg = /\d{3}/;
  // let res = reg.exec(str);
  // console.log(res);
  // let res2 = reg.exec(str);
  // console.log(res2);

  // 1-2 有g
  // let reg = /\d{3}/g;
  // let res = reg.exec(str);
  // console.log(res);
  // let res2 = reg.exec(str);
  // console.log(res2);
  // let res3 = reg.exec(str);
  // console.log(res3);
  // let res4 = reg.exec(str);
  // console.log(res4);

  // 1-3 有 ()
  // let str = "110101200204283617";
  // let reg = /(\d{2})(\d{2})(\d{2})(\d{4})(\d{2})(\d{2})(\d{4})/;
  // let res = reg.exec(str);
  // console.log(res);

  // let reg2 = /(\d{2})/g;
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));
  // console.log(reg2.exec(str));

  let str = "11010120020428361x";
  let reg = /(?:\d{6})(\d{4})(\d{2})(\d{2})(\d{3})(?:\d|x)/;
  console.log(reg.exec(str));
两种创建方式的区别
  1. 语法不一样
  2. 书写标识符的区别
  • 字面量方式,直接书写在正则的后面
  • 构造函数,以第二个参数的形式传递
  let reg = /abcd/gi;
  let reg2 = new RegExp("abcd","gi")
  console.log(reg);
  console.log(reg2);
  1. 拼接字符串
    字面量的方式不能接受拼接字符串
    构造函数的方式可以拼接字符串
  let s = "HH";
  let s2 = "MM";
  // /(HH|MM)/
  let reg = /(\s|\s2)/;
  // 字面拉闸
  console.log(reg);

  let reg2 = new RegExp("(" + s + "|" + s2 + ")");
  console.log(reg2);
  1. 基本元字符的书写
  let reg = /\d\w/;
  console.log(reg);
  let reg2 = new RegExp("\\d\\.\\w")
  console.log(reg2);
  console.log("\\n");
    /* 
      为什么构造函数要写两个\\

      + 字符串
      被引号包裹的内容都叫字符串
      放你在字符串中书写的时候,表示转义符号
      把紧挨着他的字符 转换
        - 有意义的内容转换成无意义的文本
        - 无意义的内容转换成有意义的文本
        n是一个没有意义的文本
        \n 表示换行

      + new RegExp
      第一个参数需要一个字符串
      你写的字符串就是正则内部的内容
      如果你想得到/\d\w/
      那我们写的字符串里面是 \d\w
      但是,在字符串内\是转义符
      所以当你书写'\w'的时候,\就会把w转换成有意义的特殊内容

      + 解决
      使用\可以把有特殊意义的\转成没有意义的文本\
      当你书写\\d\\w的时候,实际的字符串\d\w
    */

正则的两个特性

  1. 懒惰性
    每一次捕获都是从原始字符串的 索引0 开始的
    解决:使用全局标识符 g

  2. 贪婪性
    贪婪匹配:能拿多少拿多少,尽可能多的匹配内容
    非贪婪匹配:能拿多少拿多少,尽可能少的匹配内容

  • 需要使用非贪婪限定符号
  • 在原先的限定符号后再写一个?
  • *? 0~多次,但是0次能解决问题 就不在多
  • +?1~多次,1次能解决,就不在多
  • ??0~1次,0次能解决问题 就不在多
  • {n,}?
  • {n,m}?
    let str = '<p class="box"><span>hello world</span></p>';
    // 贪婪
    let reg = /<.+>/;
    console.log(reg.exec(str));;

    // 非贪婪
    let reg2 = /<.+?>/;
    console.log(reg2.exec(str));


es6函数相关

自执行函数

  • 一个回自己调用自己的函数
  • 当这个函数定义好后,直接被调用

语法:
=> (function(){代码})()
=》~(function(){代码})()
=》!(function(){代码})()

      // 自调用函数
      (function () {
        console.log("hello world");
      })();

      ~(function(){
        console.log("你好世界");
      })()

      !(function(){
        console.log("你好世界");
      })()

函数默认值

书写:直接在书写函数的时候,以赋值符号 给形参设置默认值就可以了
任何函数都行

      // 以前的做法
      // function add(a,b){
      //   a = a || 10;
      //   b = b || 20;
      //   return a + b;
      // }
      // let res = add(1,2);
      // console.log(res);

      // 允许你在()内设置默认值
      // function add(a = 10,b = 20){
      //   return a + b;
      // }

      // let res = add(100,200);
      // console.log(res); // 300

      // 箭头函数设置默认值时,一定要带上小括号
      let fn = (a=10) =>{
        console.log(a);
      }

      fn()


箭头函数

是 es6 语法中定义函数的一种新方式
只能用来定义函数表达式

当你把函数当作一个值赋值给另一个内容的时候,叫做函数表达式

// 之前的书写方式
// 声明式函数
function f001() {
  // 函数体
}
// 表达式函数
let foo2 = function () {
  // 函数体
};

// 箭头函数完整写法
let foo3 = (name, age) => {
  // 函数体
  console.log(name, age);
};
foo3("yh", 88);

let arr = ["aaa", "bbb", "ccc"];
arr.forEach(function (item, index, arr) {
  console.log(item, index, arr);
});
arr.forEach((item, index, arr) => {
  console.log(item, index, arr);
});

setTimeout(() => {
  console.log("abc");
}, 1000);

注意:声明式函数不可以
语法:()=>{}

  • ()写形参的位置
  • =>是箭头函数的标志
  • {}是书写代码块的位置
箭头函数的特点
  1. 可以省略小括号不写
  • 当形参只有一个的时候,可以不写小括号
  • 如果你的形参没有或者两个及两个以上,必须写
let fn1 = a => {
  console.log(a);
};
fn1(10);

let fn2 = () => {
  console.log("hello");
};
fn2();
let fn3 = (a, b, c, d) => {
  console.log(a, b, c, d);
};
fn3(1, 2, 3, 4);
  1. 可以省略大括号不写
    当你的代码只有一句话的时候,可以省略大括号,并且会自动返回这一句话的结果,否则,必须书写大括号
let fn4 = (a, b) => console.log(a, b);
let res = fn4(10, 20);
console.log(res); // undefined
let fn5 = (a, b) => a + b;
let res2 = fn4(10, 20);
console.log(res); // 30

let fn6 = (a, b) => a + b;

// 清除偶数
let arr2 = [1, 2, 3, 4, 5, 6, 7];
let res3 = arr2.filter(item => item % 2);
console.log(res3);
  1. 箭头函数没有 arguments
let fn1 = function () {
  console.log(arguments);
};
fn1(10, 20, 30, 40, 50);
let fn2 = () => {
  console.log(arguments); // 报错 未定义
};
fn2(10, 20, 30, 40, 50);
箭头函数内 this 的指向

箭头函数中 this 到底指向哪?
不适用那四条规则,而是根据外层作用域来决定
官方:外部作用域的 this
私人:书写在箭头函数外面的那个函数的 this 是谁,箭头函数的 this 就是谁。

console.log(this); // window

let obj = {
  f: function () {
    console.log("f的this", this);
  },
  foo() {
    // foo函数作用域
    // this指向obj
    // 箭头函数继承
    return () => {
      // this指向obj
      console.log("f2的this", this);
    };
  },
  f2: () => {
    console.log("f2的this", this);
  },
};
obj.f(); // obj
obj.f2(); // window
obj.foo()();

// this永远指向函数 运行时 所在的对象(作用域),而不是函数被创建时所在的对象
// js作用域
// 全局作用域
// 局部作用域(函数作用域)

let obj2 = {
  name: "obj2",
  foo() {
    return () => {
      console.log(this); // obj2
      return function () {
        console.log(this); // window
        return () => {
          console.log(this); // window
        };
      };
    };
  },
};

obj2.foo()()()();

/* 
        {
          this obj2
          返回一个fn{
            箭头函数拿上一层作用域this
            this = obj2
            返回一个fn{
              独立调用
              this = window
              返回一个fn{
                箭头函数
                继承上一层的this
                window
                this = window
              }
            }
          }
        }

        
      */

面试题

// 1.
var name = "window";
let person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  },
};
function sayName() {
  let sss = person.sayName;
  sss(); // 默认绑定 window window
  person.sayName(); // person 隐式绑定 person
  person.sayName(); // person 隐式绑定 person
  (b = person.sayName)(); // window 默认 window
  // 函数间接引用
  // 引用了 person对象中sayName
  // person对象中sayName赋值给了b
  // b()
}
sayName();
// 2.
var name = "window";
let person1 = {
  name: "person1",
  foo1: function () {
    console.log(this.name);
  },
  foo2: () => {
    console.log(this.name);
  },
  foo3: function () {
    return function () {
      console.log(this.name);
    };
  },
  foo4: function () {
    return () => {
      console.log(this.name);
    };
  },
};

let person2 = { name: "person2" };

//题目开始
person1.foo1(); // 隐式绑定 person1
person1.foo1.call(person2); // 显示绑定 person2
person1.foo2(); // 默认 外层作用域 window
person1.foo2.call(person2); // 外层 window 箭头函数 没有this call无效
person1.foo3()(); // 默认绑定 window
person1.foo3.call(person2)(); // person2 显示绑定
// window
// 改this为person2 改的是 f003
// foo3 return 出来的函数 没改
// 默认绑定 window
person1.foo3().call(person2); // 显示绑定 person2
// 这里改的就是foo3函数return出来的函数指向
// 指向了person2
person1.foo4()(); // 默认绑定 window 默认绑定 person1
// 把foo4指向person1
// return ()=>{} 拿到foo4的this
// person1 person1
person1.foo4.call(person2)(); // person2

person1.foo4().call(person2); // person1
// foo4内的this -》person1
// 箭头函数call没用 找外层 person1

// 3.
var name = "window";
function Person(name) {
  this.name = name;
  this.foo1 = function () {
    console.log(this.name);
  };
  this.foo2 = () => console.log(this.name);
  this.foo3 = function () {
    return function () {
      console.log(this.name);
    };
  };
  this.foo4 = function () {
    return () => {
      console.log(this.name);
    };
  };
}

let person1 = new Person("person1");
let person2 = new Person("person2");
person1.foo1(); // 隐式绑定 person1
person1.foo1.call(person2); // 显示绑定 person2
person1.foo2(); // person1 上层作用域
person1.foo2.call(person2); // person1 上层作用域
person1.foo3()(); //默认绑定 window
person1.foo3.call(person2)(); // 默认绑定 window
person1.foo3().call(person2); // person2
person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1

// 4.
var name = "window";
function Person(name) {
  this.name = name;
  this.obj = {
    name: "obj",
    foo1: function () {
      return function () {
        console.log(this.name);
      };
    },
    foo2: function () {
      return () => {
        console.log(this.name);
      };
    },
  };
}

let person1 = new Person("person1");
let person2 = new Person("person2");
person1.obj.foo1()(); // window
person1.obj.foo1.call(person2)(); // window
person1.obj.foo1().call(person2); // person2
person1.obj.foo2()(); // person1 obj
person1.obj.foo2.call(person2)(); // person2
person1.obj.foo2().call(person2); // obj


this

this 指向 !!重要
this 是一个关键字

是一个作用域内的关键字

  1. 要么全局使用
    this -> window
console.log(this);
console.log(window);
console.log(this === window); // true
  1. 要么函数内使用
    this 表示的是该函数的 context(执行上下文)

概念:函数内的 this,和函数如何定义没有关系,和函数在哪定义也没有关系,只和函数是如何被调用的有关。(箭头函数除外)

几种情况
  1. 普通调用
  • 函数名():普通调用
  • 里面的 this 就指向 window
function fn() {
  console.log("我是全局的函数 fn");
  console.log(this);
}

fn();
// 标准的普通调用
// 函数普通调用时,this永远指向window
  1. 对象调用
  • 对象名.函数名()
  • 对象名’函数名’
  • 数组索引
  • 该函数内的 this 指向 点 前面的内容
  • 也就是那个对象或数组
function fn() {
  console.log("我是全局的函数 fn");
  console.log(this);
}

fn(); // window
// // 标准的普通调用
// // 函数普通调用时,this永远指向window

let obj = {
  haha: 1,
  fun: fn,
};
// 调用obj内的fun函数 也就是调用全局的fn函数
obj.fun(); // 指向了obj

let arr = [fn, 1, 2];

arr[0](); // arr
  1. 定时器函数
function fn() {
  console.log("我是全局的函数 fn");
  console.log(this);
}

setTimeout(fn, 1000); // window
  1. 事件处理函数
  • 事件源.on 事件类型 = 事件处理函数
  • 事件源.addEventListener(‘事件类型’,事件处理函数)
  • 该函数内的 this 指向事件源
let box = document.querySelector(".box");

box.onclick = fn; // div
  • 函数调用时,js 会默认给 this 绑定一个值
  • this 的绑定和函数定义的位置无关
  • this 的绑定和调用方式以及调用位置有关系
  • this 是在运行时被绑定的
四个绑定规则
  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new 绑定
  1. 默认绑定
    独立函数调用
// "use strict";
// 在严格模式下,独立调用函数this指向undefined 而不是window
function foo() {
  console.log("foo函数", this);
}
foo();

// 函数定义在对象中,但是独立调用
let obj = {
  name: "zd",
  bar: function () {
    console.log("bar", this);
  },
};
// 独立调用
let baz = obj.bar;
baz(); // window

// 高阶函数
function test(cb) {
  cb();
}
test(foo); // window
test(obj.bar); // window

// 稍微复杂一点
// 全都是window
function test1() {
  console.log("复杂", this);
  test2();
}
function test2() {
  console.log("复杂", this);
  test3();
}
function test3() {
  console.log("复杂", this);
}
test1();

setTimeout(function () {
  console.log(this);
}, 1000);
  1. 隐式绑定
    通过某个对象进行调用
function foo() {
  console.log("foo函数", this);
}

let obj = {
  bar: foo,
};
obj.bar(); // obj对象

let obj1 = {
  name: "obj1",
  foo: foo,
};
let obj2 = {
  name: "obj2",
  obj1: obj1,
};
obj1.foo(); // obj1
obj2.obj1.foo(); // obj1

let bbb = obj2.obj1.foo;
bbb(); // window
  1. new 绑定
    js 中的函数可以当作类的构造函数来使用,也就是可以使用 new 关键字 new 做了哪四件事?
  2. 创建一个新的对象
  3. 新的对象执行 prototype 的连接
  4. 这个新对象会被绑定到函数的 this 上(this 的绑定就在这一步)
  5. 如果函数没有返回其他复杂数据类型,那就返回这个新对象
function Person(name) {
  console.log(this); // this指向实例对象
  this.name = name;
}
let p = new Person("zd");
console.log(p);
let p2 = new Person("qt");
console.log(p2);

function foo() {
  (this.name = "oobbjj"), console.log(this);
}
console.log(new foo());
  1. 显示绑定
    隐式绑定的前提条件,调用的对象内部必须有一个对函数的引用,如果没有这个引用,在进行调用,会报错(找不到)
let obj = {
  name: "zd",
};
function foo() {
  console.log(this.name);
}
obj.foo(); // 报错

正是对象内的函数引用,简介的让 this 绑定到了这个对象上
我们不希望在对象内部存着函数引用,同时又希望在这个对象上进行强行调用。
js 的所有函数都可以使用 call/bind/apply 方法

let obj = {
  name: "zd",
};
function foo() {
  console.log(this.name);
}
obj.foo(); // 报错
foo(); // oobbjj
foo.call(obj); // yh

改变 this 指向

强行改变 this 指向:不管你本身指向哪里,我让你指哪,你就得指哪
call/bind/apply

call
  • 语法:跟随在函数名后面调用
    • 函数.call(this 指向)
    • 对象名.函数.call(this 指向)
      意义:修改函数内 this 的指向
  • 参数:
    • 第一个参数:函数内的 this 指向
    • 第二个参数:依次传递给函数的实参
  • 特点:会立即调用该函数
function fn(a, b) {
  console.group("fn函数内部打印");
  console.log("this=>", this);
  console.log("a=>", a);
  console.log("b=>", b);
  console.groupEnd();
}

let obj = {
  name: "我是obj对象",
};
let arr = [10, 20, 30, 40, 50];

// 利用call改变指向
// obj就是fn函数内的this
// 100就是给fn的第一个实参,赋值给了a
// 200就是给fn的第一个实参,赋值给了b
fn.call(obj, 100, 200);
/* 
      this-》obj
      a-》100
      b-》200
    */
fn.call(arr, 1000, 2000);
/* 
    this->arr
    a->1000
    b->2000
   */
apply
  • 语法:和 call 一样
  • 意义:和 call 一样
  • 参数:
    • 第一个参数:函数内的 this 指向
    • 第二个参数:数组/伪数组(arr)都行,arr 里面的每一项会依次传递给函数的实际参数
  • 特点:立即调用函数
  • 特殊作用:改变函数传递参数的方式
function fn(a, b) {
  console.group("fn函数内部打印");
  console.log("this=>", this);
  console.log("a=>", a);
  console.log("b=>", b);
  console.groupEnd();
}

let obj = {
  name: "我是obj对象",
};
let arr = [10, 20, 30, 40, 50];

// 因为是利用apply调用
// obj就是fn内的this
// 第二参数传递一个arr数组
// arr[0]是给fn函数的第一个实参,赋值给了形参a
// arr[1]是给fn函数的第二个实参,赋值给了形参b
fn.apply(obj, [100, 200]);

// apply特殊作用
let res = Math.max(1, 2, 3, 4, 5, 6);
console.log(res);
console.log(Math.max);
let arr2 = [10, 90, 101, 312, 800, -100, 50];

let res2 = Math.max.apply(null, arr2);
console.log(res2);
bind
  • 语法:跟随在函数名后面调用
    • 函数名.bind()
    • 对象名.函数.bind()
      意义:修改函数内的 this 指向
  • 参数:
    • 第一参数:函数内的 this
    • 第二参数:依次给函数传递的实参
  • 特点:
    • 不会立即调用该函数,而是返回一个新的函数
    • 新的函数就是 this 被改变之后的
  • 特殊作用:改变一些不要立即执行的函数内的 this
function fn(a, b) {
  console.group("fn函数内部打印");
  console.log("this=>", this);
  console.log("a=>", a);
  console.log("b=>", b);
  console.groupEnd();
}

let obj = {
  name: "我是obj对象",
};
let arr = [10, 20, 30, 40, 50];

let bb = fn.bind(obj, 100, 200);
console.log(bb);
bb(); // obj 100 200

// bind的特殊作用
// 给定时器函数改变this指向
// call、apply方法都不行,因为他们都会立即调用该函数
// 定时器就失去效果了

// 利用bind调用fn函数
// obj就是fn函数内的this
// 注意:因为是bind,不会执行fn,而是把fn复制一份,把内部的this改变指向obj
setTimeout(fn.bind(obj, 100, 200), 1000);
规则的优先级
  1. 默认规则的优先级最低
  2. 显示绑定优先级高于隐式绑定
  3. new 绑定高于隐式绑定
  4. new 绑定高于 bind
  5. bind 高于 call apply

由高到低
new -> bind -> call\apply -> 隐式 -> 默认

// 比较优先级
function foo() {
  console.log("foo", this);
}
// 1. 显示绑定优先级高于隐式绑定
// 1-1 call/apply高于隐式
let obj = { foo: foo };
obj.foo(); // obj
obj.foo.call("abc"); // String {'abc'}
obj.foo.apply("abc"); // String {'abc'}

// bind高于隐式
let bar = foo.bind("abc");
let obj1 = {
  name: "ooo",
  bar: bar,
};
obj1.bar(); // String {'abc'}
// 2. new 绑定高于隐式绑定
let obj2 = {
  name: "obj2",
  foo1: function () {
    console.log("foo", this);
  },
};

new obj2.foo1(); // foo1 {}

// 3. new 绑定高于 bind
let bindFn = foo.bind("bbb");
new bindFn(); // foo {}

// 4. bind高于call apply
bindFn.call("call"); // String {'bbb'}
bindFn.apply("apply"); // String {'bbb'}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值