Skip to content

回顾 内置对象与内置对象(Day11 1/3) ​

本页对应 Day11_BOM&DOM,共 7 段视频,约 1 小时 56 分。

学习目标 ​

  1. 能归纳 Number、String、Math、Date、Array 的常用实例方法与构造函数属性,并在给定需求中正确选用对应 API。
  2. 能使用 split、map、join 和三元表达式将短横线分隔字符串转换为小驼峰字符串,并封装为可复用函数。
  3. 能使用 split、reverse、join 实现字符串翻转,并封装为接收字符串、返回翻转结果的函数。
  4. 能使用 Math.random、Math.floor 编写随机取出数组元素的函数,并验证索引范围正确。
  5. 能使用 Date 的 getFullYear、getMonth、getDate、getHours、getMinutes、getSeconds 拼接日期时间字符串,并对个位数补零。
  6. 能区分并演示 Function 实例的 length、call、apply、bind,说明 this 绑定、参数传递方式及返回值差异。

知识点 ​

本节材料实际围绕内置对象复习、字符串/数组/日期案例以及 Function 实例方法展开,转写稿中没有涉及 BOM、DOM 的具体 API,因此下面按材料整理,不补写未讲内容。

字符串短横线转小驼峰 ​

为什么需要:接口参数、类名、CSS 属性常出现 get-element-by-id 这类短横线格式,业务中常要转成 getElementById。怎么想:先用短横线把字符串切成数组,再让第一个单词全小写,其余单词首字母大写,最后拼回字符串。怎么做:

js
function toCamel(str) {
  return str.split('-').map(function (item, index) {
    if (index === 0) return item.toLowerCase();
    return item[0].toUpperCase() + item.slice(1).toLowerCase();
  }).join('');
}
console.log(toCamel('get-element-by-id')); // getElementById

易错点:split('-') 不传分隔符时,数组只有一个元素;item[0].toUpperCase() 只处理首字母,后面要接 slice(1);链式调用按从左到右执行,item.slice(1).toLowerCase() 中 toLowerCase 的调用者是 slice 的返回值,不是 item 本身。

字符串翻转 ​

为什么需要:回文判断、倒序展示等场景需要翻转字符串。怎么想:字符串没有数组的 reverse,但可以 split('') 转成字符数组,翻转后再 join('')。怎么做:

js
function reverseString(str) {
  return str.split('').reverse().join('');
}
console.log(reverseString('hello高小乐'));

易错点:split('') 必须传空字符串,否则整个字符串会作为一个元素;reverse() 会改变原数组,但这里原数组是临时产生的;手写时注意 reverse 拼写,转写稿中就出现过手误后改回。

随机取数组元素 ​

为什么需要:随机点名、抽奖、随机推荐都要求从数组中随机取一项。怎么想:先得到 0 到 arr.length - 1 的随机整数,再把它当索引。怎么做:

js
function randomRead(arr) {
  return arr[Math.floor(Math.random() * arr.length)];
}
console.log(randomRead(['牛姥姥', '独孤姥姥', '刘姥姥']));

易错点:Math.random() 范围是 [0,1),乘以 arr.length 后要 Math.floor 向下取整;不要再额外减一,因为 Math.random() 取不到 1,最大整数正好是 length - 1。

日期时间字符串与补零 ​

为什么需要:页面展示日期时通常要求 2021-03-04 19:02:05 这种固定格式。怎么想:从 Date 对象分别取年月日时分秒,月份要 +1,个位数补 0,最后拼接。怎么做:

js
let d = new Date();
let y = d.getFullYear();
let M = d.getMonth() + 1;
let D = d.getDate();
let H = d.getHours();
let m = d.getMinutes();
let s = d.getSeconds();

M = M < 10 ? '0' + M : M;
D = D < 10 ? '0' + D : D;
H = H < 10 ? '0' + H : H;
m = m < 10 ? '0' + m : m;
s = s < 10 ? '0' + s : s;

console.log(`${y}-${M}-${D} ${H}:${m}:${s}`);

易错点:getMonth() 返回 0~11,必须加一;getDay() 是星期几,日期要用 getDate();补零可用三元表达式,判断的是“小于 10”,不要漏掉 0 本身。

Function 的 length、call、apply、bind ​

为什么需要:函数也是对象,call/apply/bind 可以调用函数并指定函数内部 this,是后续封装、借用方法、函数柯里化的基础。怎么想:length 看形参数量;call 和 apply 都会立即调用函数,区别是传参形式;bind 不立即调用,而是返回一个被固定 this 的新函数。怎么做:

js
function func(a, b) {
  console.log(this, a * b);
  return a + b;
}

console.log(func.length); // 2

func.call({ name: '高小乐' }, 10, 20);
func.apply({ name: '高小乐' }, [10, 20]);

const fn = func.bind([1, 2, 3], 100);
fn(200); // this 固定为数组,a 固定为 100

易错点:call 从第二个参数开始依次传给函数;apply 第二个参数必须是数组;不传 this 时普通调用中 this 是 window;bind 返回新函数,必须再调用才会执行。转写稿还演示了用 call 借用数组 forEach 遍历字符串,但前提是理解 forEach 内部按 this 遍历,初学阶段先掌握 this 指定规则即可。

先看总览:五个内置对象 ​

本节先回顾昨天讲过的内置对象。转写稿里反复强调:每个内置对象都要分成两组看——实例的属性/方法,以及构造函数本身的属性/方法。布尔对象既没属性也没方法,所以不展开。总览代码保留如下:

1. Number
2. String
3. Math 
4. Date
5. Array

为什么需要:内置对象是 JS 自带工具箱,写业务时不用重复造轮子。
怎么想:先按对象分类,再记“实例方法”还是“构造函数方法”。
怎么做:逐个过 Number、String、Math、Date、Array,重点看数组。
易错点:把 String.fromCharCode 这种构造函数方法误写成实例方法;把 Math 当构造函数 new Math(),其实它本身就是对象,直接用。

Number:数值格式化与边界值 ​

为什么需要:金额、小数位、进制转换都要处理数字。
怎么想:实例方法作用于某个数字;构造函数属性表示边界。
怎么做:实例常记 toFixed() 保留小数位、toString() 转字符串;构造函数记 Number.MAX_VALUE、Number.MIN_VALUE。
易错点:toFixed() 返回的是字符串;MAX_VALUE 不是“最大安全整数”,安全整数是 Number.MAX_SAFE_INTEGER。

String:查找、截取、大小写、分割与编码 ​

为什么需要:表单值、URL、类名、文本处理都离不开字符串。
怎么想:先分清“查找返回位置”还是“返回布尔”;截取要记区间是否包含结束位;分割会得到数组。
怎么做:indexOf/lastIndexOf 返回位置,找不到返回 -1;includes 返回布尔;slice(start,end) 和 substring(start,end) 包含起始、不包含结束;substr(start,length) 第二参数是长度;toUpperCase/toLowerCase 转大小写;split(分隔符) 分割成数组;charCodeAt 取 Unicode 编码,String.fromCharCode 是逆运算。
易错点:split() 不传分隔符时,新数组只有一个元素;substr 第二参数不是结束位置。

转写稿里的“短横线转小驼峰”可以这样推:先 split('-') 得到四个单词,再让第一个单词小写,其余单词首字母大写,最后 join('')。调试时先打印数组,确认分割成功,再处理每个元素:

js
function toCamel(str) {
  return str.split('-').map((item, index) => {
    return index === 0
      ? item.toLowerCase()
      : item[0].toUpperCase() + item.slice(1).toLowerCase();
  }).join('');
}

Math:数学计算与随机数 ​

为什么需要:取整、绝对值、平方根、最大最小值、随机抽奖都要用。
怎么想:Math 不是构造函数,直接 Math.方法();随机数范围是 [0,1)。
怎么做:常记 Math.PI、abs、sqrt、pow、max、min、floor、ceil、round、random。随机取数组元素公式是 Math.floor(Math.random() * arr.length),不要写 arr.length - 1 再乘。
易错点:Math.random() 取不到 1;取整三个方法方向不同,round 是四舍五入。

js
function randomRead(arr) {
  return arr[Math.floor(Math.random() * arr.length)];
}

Date:日期时间与补零 ​

为什么需要:订单时间、倒计时、日志都要格式化日期。
怎么想:先 new Date() 得对象,再分别 getFullYear/getMonth/getDate/getHours/getMinutes/getSeconds;月份从 0 开始。
怎么做:个位数补零用三元:m < 10 ? '0' + m : m。
易错点:getMonth() 要加 1;getDate() 是几号,getDay() 是星期几;getTime() 是时间戳。

Array:访问器、修改器、回调方法 ​

为什么需要:数组是本节重点,列表渲染、筛选、累计都靠它。
怎么想:先分“访问器方法不改原数组”和“修改器方法改原数组”;再记七个回调方法:forEach/filter/map/every/some/reduce/reduceRight。
怎么做:concat/join/slice/indexOf/lastIndexOf 是访问器;push/pop/shift/unshift/splice/sort/reverse 是修改器。forEach 无返回值;filter 返回回调为 true 的元素组成的新数组;map 返回每次回调返回值组成的新数组;every 全真才真;some 一真就真;reduce 回调参数是 (prev, current, index, array),第二参数是初始值,不传时从第二元素开始、第一元素当 prev;reduceRight 从右往左。
易错点:forEach 不能 break;filter/map 都返回新数组;reduce 空数组不传初始值会报错;sort 默认按字符串排序。

字符串翻转可用数组方法推导:split('') 变单字符数组,reverse() 翻转,join('') 拼回字符串:

js
function reverseStr(str) {
  return str.split('').reverse().join('');
}

补充:Function 实例的 call、apply、bind ​

转写稿后半还补充了 Function 实例方法。为什么需要:手动控制函数里的 this,并复用函数。怎么想:call/apply 立即调用函数;bind 不立即调用,返回绑定了 this 的新函数。怎么做:call(thisArg, 参数1, 参数2...);apply(thisArg, [参数数组]);bind(thisArg, 预设参数...)。易错点:apply 第二参数必须是数组;bind 返回新函数,要再调用才执行。

这一节要解决什么问题 ​

这一节不是只背 API,而是把“内置对象”串起来:先分清实例方法和构造函数自身属性,再用字符串、数组、日期、函数实例做案例。转写稿回顾了 Number、String、Math、Date、Array 五个内置对象,Boolean 因既无属性也无方法不展开。核心目标是:知道为什么用、怎么想、怎么写,以及常见坑。

先分清“实例”和“构造函数本身” ​

为什么需要:同一个对象上,实例属性和构造函数自身属性容易混。怎么想:先问“这个方法是谁调用的”。怎么做:Number 实例有 toFixed、toString,构造函数自身有 MAX_VALUE、MIN_VALUE;String 实例有 indexOf、slice、split、toUpperCase、toLowerCase、charCodeAt,构造函数自身有 String.fromCharCode;Math 本身就是对象,直接用 Math.PI、Math.abs、Math.sqrt、Math.pow、Math.max、Math.min、Math.floor、Math.ceil、Math.round、Math.random;Date 需要 new Date();Array 实例方法分访问器和修改器,重点掌握 forEach/filter/map/every/some/reduce/reduceRight。易错点:Math 不需要 new;String.fromCharCode 不是实例方法;数组的 slice 和字符串的 slice 同名但不是同一个对象上的方法。

字符串转换案例:短横线转小驼峰 ​

为什么需要:后端或类名常出现 get-element-by-id,前端有时要转成 getElementById。怎么想:先按 - 切成数组,再处理每个单词,最后拼回字符串。怎么做:转写稿先写直白版,再写链式版。核心代码:

js
function toCamel(str) {
  return str.split('-').map(function (item, index) {
    if (index === 0) {
      return item.toLowerCase();
    }
    return item[0].toUpperCase() + item.slice(1).toLowerCase();
  }).join('');
}

console.log(toCamel('get-element-by-id'));

推导过程:split('-') 得到四个单词;map 返回新数组;第一个元素不首字母大写,后面的元素取 item[0].toUpperCase(),再拼 item.slice(1).toLowerCase();最后 join('') 去掉逗号。易错点:split 不传分隔符会得到只有一个元素的数组;slice(1) 从第二个字符截到最后;map 不改变原数组,返回新数组;链式调用从左到右执行,slice 的返回值再调用 toLowerCase。

字符串翻转案例 ​

为什么需要:练习数组修改器方法和链式调用。怎么想:字符串没有 reverse,但数组有,所以先转数组。怎么做:

js
function reverseString(str) {
  return str.split('').reverse().join('');
}

console.log(reverseString('hello高小乐'));

易错点:split('') 必须传空字符串,否则整个字符串只成一个元素;reverse() 会直接改变调用它的数组;转写稿里曾手误多写一个 r 导致报错,改回 reverse 即可;最后必须 join(''),否则得到的是数组。

随机取数组元素 ​

为什么需要:随机点名、抽奖等。怎么想:先得到 0 到 arr.length - 1 的随机整数,再当索引。怎么做:

js
function randomRead(arr) {
  return arr[Math.floor(Math.random() * arr.length)];
}

易错点:Math.random() 范围是 [0,1),乘数组长度后要 Math.floor;不要用 Math.round,边界概率不均;索引最大是 length - 1。

日期时间字符串 ​

为什么需要:把日期对象格式化成 YYYY-MM-DD HH:mm:ss。怎么想:先分别取年月日时分秒,再对个位数补零。怎么做:

js
function formatDate(d) {
  var y = d.getFullYear();
  var m = d.getMonth() + 1;
  var day = d.getDate();
  var h = d.getHours();
  var min = d.getMinutes();
  var s = d.getSeconds();

  m = m < 10 ? '0' + m : m;
  day = day < 10 ? '0' + day : day;
  h = h < 10 ? '0' + h : h;
  min = min < 10 ? '0' + min : min;
  s = s < 10 ? '0' + s : s;

  return y + '-' + m + '-' + day + ' ' + h + ':' + min + ':' + s;
}

易错点:getMonth() 返回 0–11,必须加 1;getDay() 是星期几,不是日期;补零只处理月、日、时、分、秒,年份不补;转写稿调试时秒到 60 会自然变成下一分钟,补零逻辑仍要按个位数判断。

Function 实例:length、call、apply、bind ​

为什么需要:函数也是对象,理解 this 和调用方式。怎么想:length 看形参数量;call/apply 调用函数并设置 this;bind 返回设置了 this 的新函数,不立即调用。怎么做:

js
function func(a, b) {
  console.log(this, a * b);
  return a + b;
}

func.call({ name: '高考乐' }, 10, 20);
func.apply({ name: '高考乐' }, [10, 20]);

var fn = func.bind({ name: '高考乐' }, 100);
fn(200); // 第一个参数固定为 100

易错点:call 后续参数逐个传,apply 第二个参数必须是数组;bind 不执行函数,只返回新函数;length 是形参数量,不是实参数量;bind 可预设参数,预设后调用时只传剩余参数。转写稿还展示了用 call/apply 把数组 forEach 的 this 改成字符串来遍历字符,这依赖 forEach 内部按 this 遍历,属于进阶理解,先掌握规则再使用。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一:短横线字符串转小驼峰 + 字符串翻转 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>字符串处理</title>
</head>
<body>
  <script>
    // 1. 短横线分隔的字符串 -> 小驼峰
    function toCamel(str) {
      return str
        .split('-')                      // 以短横线为分隔符,分割成数组
        .map(function (item, index) {    // 对每个单词做处理,返回新数组
          return index === 0
            ? item.toLowerCase()                                  // 第一个单词:全小写
            : item[0].toUpperCase() + item.slice(1).toLowerCase(); // 其余:首字母大写 + 剩余小写
        })
        .join('');                       // 数组 -> 字符串,空串做分隔符
    }

    // 2. 字符串翻转
    function reverseString(str) {
      return str.split('').reverse().join('');
    }

    console.log(toCamel('get-element-by-id'));        // getElementById
    console.log(toCamel('set-name-by-class-name'));   // setNameByClassName
    console.log(reverseString('hello高小乐'));        // 乐小高olleh
  </script>
</body>
</html>

关键行与易错点

  • split('-') 必须传分隔符,不传的话新数组只有一个元素(整个字符串),后面 map 就白做了。
  • map 返回的是新数组,元素个数和原数组一致,内容取决于回调函数的 return;forEach 没有返回值,不能拿来做这一步。
  • item[0] 取首字符,item.slice(1) 取剩余部分;slice 只传一个参数就截取到末尾,起始位置的字符包含在内、结束位置不包含。
  • 三元运算符 index === 0 ? A : B 中,index === 0 说明是第一个单词,它不参与大写处理;判断写反是常见错误。
  • 链式调用 split().map().join() 从左到右执行,前一个方法的返回值继续调用后一个方法,中间不需要再声明变量。
  • join('') 参数不能省,省掉会用逗号拼接,结果变成 get,Element,By,Id。

示例二:日期时间字符串(个位数补零) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>日期时间字符串</title>
</head>
<body>
  <script>
    function formatDate() {
      var d = new Date();          // 当前日期时间对象

      var y   = d.getFullYear();   // 年
      var m   = d.getMonth() + 1;  // 月:0~11,必须 +1
      var day = d.getDate();       // 日(getDay 是星期几)
      var h   = d.getHours();      // 时
      var min = d.getMinutes();    // 分
      var s   = d.getSeconds();    // 秒

      // 个位数前面补 0(年不需要)
      m   = m   < 10 ? '0' + m   : m;
      day = day < 10 ? '0' + day : day;
      h   = h   < 10 ? '0' + h   : h;
      min = min < 10 ? '0' + min : min;
      s   = s   < 10 ? '0' + s   : s;

      return y + '-' + m + '-' + day + ' ' + h + ':' + min + ':' + s;
    }

    console.log(formatDate());     // 例:2023-04-05 09:08:07
  </script>
</body>
</html>

关键行与易错点

  • getMonth() 返回 0~11,一定要 +1,这是最容易踩的坑。
  • 取“日”用 getDate();getDay() 返回的是星期几(0~6),名字很像但完全不是一个东西。
  • 变量名不要撞车:getMinutes() 和 getMonth() 都以 M 开头,转写稿里就用 min 区分。
  • 补零统一用三元运算符 x < 10 ? '0' + x : x,赋值回原变量;忘了赋值(只写表达式)页面不会有任何变化。
  • 拼接时数字会自动转成字符串,所以不用手动 toString();但变量顺序写错、少写连接符(如 -、:)是常见失误。
  • 想验证补零是否生效,把秒数盯到 0~9 那几秒刷新一下即可。

示例三:Function 的 call / apply / bind 与“借用数组方法” ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Function 实例方法</title>
</head>
<body>
  <script>
    function func(a, b) {
      console.log('this =', this, '| a =', a, '| b =', b);
      return a + b;
    }

    // 1. call:调用函数,第一个参数设置 this,后面依次是函数的实参
    console.log('call 返回值:', func.call({ name: '曹操' }, 10, 20));

    // 2. apply:调用函数,第一个参数设置 this,第二个参数是数组,数组元素作为实参
    console.log('apply 返回值:', func.apply({ name: '刘备' }, [8, 5]));

    // 3. bind:不调用函数,返回一个 this 被固定住的新函数
    var fn01 = func.bind({ name: '孙权' });      // this 固定,参数照常传
    console.log('bind 返回值:', fn01(3, 4));

    var fn02 = func.bind({ name: '孙权' }, 1000); // 顺带把第一个实参固定成 1000
    console.log('bind 预设参数:', fn02(2));      // 只需传剩下的那个

    // 4. 借用数组方法:字符串没有 forEach,用 call 把 this 换成字符串
    Array.prototype.forEach.call('hello', function (item, index) {
      console.log('下标', index, '字符', item);
    });

    // 5. 借用 reduceRight 实现字符串翻转
    var str = 'hello高小乐';
    var res = Array.prototype.reduceRight.call(str, function (prev, item) {
      return prev + item;                        // 注意这里是字符串拼接
    });
    console.log('reduceRight 翻转:', res);      // 乐小高olleh
  </script>
</body>
</html>

关键行与易错点

  • call 的第一个参数就是函数内部的 this,从第二个参数开始一一对应函数的形参;参数个数不限。
  • apply 只有两个参数,第二个必须是数组(传数字会报错),数组里的元素依次对应函数的形参。
  • bind 不会调用函数,只是返回一个 this 已被固定的新函数,必须再自己加小括号调用一次,忘了调用就没有任何输出。
  • bind 除了固定 this,还能预先固定前面的实参(如 fn02 的 1000),调用时只传剩下的参数,容易算错参数位置。
  • Array.prototype.forEach 本质就是数组原型上的一个函数,call 的作用是把它的 this 从数组换成字符串,从而“借用”它来遍历字符串——forEach 内部遍历的是 this,所以换成谁就遍历谁。
  • 回调函数里 prev + item 是字符串拼接不是相加,因为 prev 是字符;reduceRight 不传第二个参数时,最后一个元素作为初始的 prev,从后往前累计。
  • 打印 this 时如果是对象会显示内容,直接 func() 调用时 this 是 window,对比着看更容易理解 this 被改写前后的差别。

常见错误 ​

  • 字符串 split 不传分隔符 → 误以为会按字符拆分,实际得到单元素数组 → 明确传入 '' 或 '-' 等分隔符,并先打印中间数组验证。
  • 链式调用中分不清调用者 → 把 item.slice(1).toLowerCase() 误认为 toLowerCase 仍由 item 调用 → 按从左到右看返回值:slice 的结果再调用 toLowerCase。
  • forEach 中处理首字母大写时漏判索引 → 第一个单词也被大写,结果不符合小驼峰 → 用 index === 0 分支单独返回原词/小写词。
  • 随机取数组元素范围写错 → 索引可能越界或取不到最后一个元素 → 用 Math.floor(Math.random() * arr.length),不要再额外减一。
  • 日期格式化个位数不补零 → 出现 2021-4-5 9:3:2 这类格式 → 对月、日、时、分、秒统一做 <10 补 0,月份记得 +1。
  • call / apply / bind 混用 → 参数形式或是否立即执行判断错误 → 记住:call 参数逐个传,apply 第二参数传数组,bind 返回新函数不立即执行。
  • 数组修改器方法误当访问器 → 以为原数组不变,后续结果错乱 → 使用 push/pop/splice/reverse/sort 后检查原数组,必要时先复制。

练习 ​

  1. 封装 toCamelCase(str):把 get-element-by-id 转为小驼峰;要求分别用 forEach 和 map + join 两种思路实现。
  2. 封装 reverseString(str):用 split('')、reverse()、join('') 完成字符串翻转,并打印每一步中间结果。
  3. 封装 randomPick(arr):从数组中随机返回一个元素;补充测试空数组时的处理策略。
  4. 封装 formatDate(date):输出 YYYY-MM-DD HH:mm:ss,要求月、日、时、分、秒不足两位补 0。
  5. 阅读一段 call/apply/bind 调用代码,写出每次函数执行时 this 指向、参数值和返回值,并说明三者差异。

原视频对照 ​

#视频时长
1回顾17:20
2案例讲解-字符串转换29:28
3案例讲解-字符串翻转03:45
4案例讲解-随机取出数组中的元素03:54
5案例讲解-日期时间字符串04:58
6内置构造函数-Function0146:17
7内置构造函数-Function0210:58

小结 ​

  • 先判断每一步的返回值和调用者,再写链式调用,能减少大部分“看起来对、结果错”的问题。
  • 数组回调方法重点看返回值:forEach 无返回、filter/map 返回新数组、every/some 返回布尔、reduce 看累计结果。
  • 修改器方法与访问器方法要分开记,调试时先打印原数组和新数组。
  • call/apply/bind 的核心是控制 this,同时注意参数传递形式和是否立即执行。
  • 日期格式化、随机取值、字符串转换这类案例,先写直白版本,再逐步简化和封装。
  • 封装函数时补标准注释,明确参数、返回值和边界情况,避免只验证“正常输入”。
  • 公式和链式写法不要死背,多打印中间结果、多改参数验证,理解执行顺序比记结论更重要。