查漏补缺 - JS之一

1,程序流程控制

顺序,条件分支,循环。

2,变量,字面量,表达式

1,字面量:直接书写的数据

var a = 1 // 1 是字面量,下面同理
var a = 'abc'
var a = {
    
    
  name: 'xxx'
}

2,a = b = 7,是将 b = 7 这个表达式的运算结果赋值给 a

3,\r 回车,\n 换行。2个效果一样,打字机历史遗留问题。一般都使用 \r\n 来表示换行。

3,函数

函数的本质,对流程进行切割。

原则之一 DRY (Don’t repeat yourself)

虽说要避免重复,但有时为了可读性,应该放弃这个原则。

4,数据的存储和传递

1,代码的实现顺序,先将对象做出来,再赋值给变量。所以无法在对象中访问变量,因为还没有被定义。

var obj = {
    
     name: '下雪天的夏风', a: obj.name } // 报错

但函数不一样,myName 是函数的地址引用。

var obj = {
    
    
  name: '下雪天的夏风',
  myName: function () {
    
    
    console.log(obj.name)
  }
}
obj.myName()

相当于先定义了一个函数(对象中的是匿名函数),obj.myName 被赋值的是引用地址。

function _myName() {
    
    
  console.log(obj.name)
}

var obj = {
    
    
  name: '下雪天的夏风',
  myName: _myName
}
obj.myName()

在对象的方法中直接使用对象名,是有隐患的。
比如将对象 obj 赋值给另一个对象 obj2 ,此时对象方法中使用的还是 obj,就出问题了。所以应该用 this,后面会介绍。

2,将一个变量赋值给另一个变量时:

  • 原始类型,复制的是值。
  • 引用类型,复制的是地址。

通过一个函数来实现交换2个变量的值。此题无解,因为在函数中改变的是形参。

// n1, n2 并不会变,因为交换的是形参。
function swap(a, b) {
    
    
  let temp = a;
  a = b;
  b = temp;
}

let n1 = 1, n2 = 2;

swap(n1, n2);
console.log(n1, n2); // 1, 2

修改了形参 obj 指向的引用地址。和 myObj 断开联系。

function pick(obj) {
    
    
  const newObj = {
    
     c: 3 };
  obj = newObj;
}

const myObj = {
    
     a: 1, b: 2 };
pick(myObj);
console.log(myObj); // { a: 1, b: 2 }

京东面试题。

  • 数组也是对象,只不过属性 key 是 index 而已。注意和上面修改 obj 的区别:在函数中直接操作了数组时,所以会影响原数组。
  • push 方法相当于新增一个键值对。
// 下面代码输出什么
var foo = {
    
    
  n: 1
}

var arr = [foo]

function method1(arr) {
    
    
  var bar = arr[0]
  arr.push(bar) // arr = [foo, foo]
  bar.n++ // foo = {n: 2}
  arr = [bar]
  arr.push(bar)// arr = [foo, foo]
  arr[1].n++ // foo = {n: 3}
}

function method2(foo) {
    
    
  foo.n++ // foo = {n: 4}
}

function method3(n) {
    
    
  n++ // 没什么用
}

method1(arr)
method2(foo)
method3(foo.n)

console.log(foo.n, arr.length) // 4, 2

5,函数作用域

1,内部函数作用域能访问外部变量,取决于函数定义的位置,和调用无关。

var a = 1;

function fun() {
    
    
  a++;
}

function fun2() {
    
    
  var a = 3;
  fun();
  console.log(a); // 3
}

fun2();
console.log(a); // 2

2,作用域内定义的变量、函数声明会提升到作用域顶部。

console.log(a); // undefined
var a = 1
// 其实是
var a 
console.log(a);
a = 1
console.log(b); // undefined
var b = function () {
    
    };
// 其实是
var b 
console.log(b);
b = function () {
    
    };

函数作用域中也一样。

var a = 1
function fun() {
    
    
  console.log(a); // undefined
  var a = 2
}
fun()

3,闭包:让开发者可以从内部函数访问外部函数的作用域。

6,全局对象

1,给未声明的变量赋值,实际上是给全局对象的属性赋值。

a = 1
console.log(window.a) // 1

function fun() {
    
    
  b = 2
}
fun()
console.log(window.b) // 2

2,所有的全局变量,全局函数都会附加全局对象上。这称为全局污染(暴露)

如果只是简单的放到一个函数中,那这个函数还是会附加到全局对象上,依然会污染。而且函数的作用本身是为了提取重复的部分,结果函数体内执行的还是全局代码。

可以通过 IIFE(立即执行函数) 来避免。强行改变作用域。

不想暴露出的变量就放在函数体内,想暴露出的 return 即可。

// 可以通过表达式的方式使用函数
var fun = function() {
    
    }
fun()

// 简化下,就变成 IIFE
(function() {
    
    })()

// 暴露指定内容
const temp = (function() {
    
     return xxx })()

7,构造函数,原型,this,原型链,继承

参考

8,标准库

1,常用

当把一个原始类型(String, Number, Boolean)当做对象使用时,js 会自动将其转换为对应包装类的实例

var a = '1'
console.log(a.toString())

1,String.prototype.padStart()

MDN - padStart

用途之一:日期补0

function useAddZero(num) {
    
    
  return num < 10 ? '0' + num : num
}
// 或
function useAddZero(num) {
    
    
  return num.toString().padStart(2, '0')
}

2,Number.prototype.toString()可以转换进制,比如颜色的 rgb 转为16进制。

var a = 255
a.toString(16) // ff

3,Number.prototype.toFixed() 可以四舍五入保留小数。返回String

4,Math.round()四舍五入返回整数。返回 Number。

不过也可以配合其他 Math 方法实现小数舍入

5,Math.random() 随机数。

举例

/**
 * 得到一个指定范围内的随机整数
 * @param {number} min 范围的最小值
 * @param {number} max 范围的最大值(无法取到最大值)
 * @return {number} 范围内的随机整数 [min, max)
 */
function getRandom(min, max) {
    
    
  // Math.random()   [0, 1)
  // Math.random() * (max - min)   [0, max - min)
  // Math.random() * (max - min) + min   [min, max)
  return Math.random() * (max - min) + min
}

/**
 * 得到一个指定长度的随机字符串。字符串包括:数字和字母
 * @param {number} length 字符串的长度
 * @return {number} 随机字符串
 */
function getRandomString(length) {
    
    
  const str = 'abcdefghijklmnopqrstuvwxyz1234567890'
  let result = ''
  for (let i = 0; i < length; i++) {
    
    
    result += str[Math.floor(getRandom(0, str.length))]
  }
  return result
}

另一种实现方式:

1,36进制是包括 0-9 a-z 的字符。
2,结合 Number.prototype.toString() 可以指定进制。

// 这种方式最多只有 11 位字符
function getRandomString2(length) {
    
    
  return Math.random().toString(36).substring(2, length) // 截取 0.
}

2,日期

MDN - Date

除了常用的 Date.prototype.getTime() 等之外,还有 toLocaleDateString 等方法,来返回本地的时间格式。

const date = new Date()

console.log(date.toLocaleString("en-US")); // 8/30/2023, 3:57:16 PM
console.log(date.toLocaleString("zh-CN")); // 2023/8/30 15:57:16
// 不传参数,默认使用主机所在区域
console.log(date.toLocaleString()); // 2023/8/30 15:57:16

举例,格式化时间

/**
 * 将日期格式为字符串
 * @param {Date} date 日期
 * @param {string} format 格式化规则
 * @return {string} 日期字符串
 */
function formatDate(date, format) {
    
    
  const year = date.getFullYear()
  const month = (date.getMonth() + 1).toString().padStart(2, '0')
  const day = date.getDate().toString().padStart(2, '0')
  const hour = date.getHours().toString().padStart(2, '0')
  const minute = date.getMinutes().toString().padStart(2, '0')
  const second = date.getSeconds().toString().padStart(2, '0')
  const milliseconds = date.getMilliseconds()

  return format
    .replace('yyyy', year)
    .replace('MM', month)
    .replace('dd', day)
    .replace('HH', hour)
    .replace('mm', minute)
    .replace('ss', second)
    .replace('ms', milliseconds)
}

// 2023年08月30日 16时11分33秒327毫秒
console.log(formatDate(new Date(), 'yyyy年MM月dd日 HH时mm分ss秒ms毫秒'))

3,数组

1,伪数组转真数组

const obj = {
    
    
  0: 'a',
  1: 'b',
  length: 2
}
// slice 切分数组,并返回新数组
const arr = Array.prototype.slice.call(obj)
// 或
const arr = [].slice.call(obj)
console.log(arr) // [ 'a', 'b' ]

2,删除数组中的元素

在循环中使用 splice 删除数组元素时,数组整体长度会减少,所以下标也得跟随变化。
否则会出问题(因为下标不变的话,会跳过下一个元素)

const arr = [3, 1, 'a', 'b', 'c', 7, 9, 0]

for (let i = 0; i < arr.length; i++) {
    
    
  if (typeof arr[i] === 'string') {
    
    
    arr.splice(i, 1)
    i-- // 没有这行语句,则结果为 [ 3, 1, 'b', 7, 9, 0 ]
  }
}
console.log(arr) // [ 3, 1, 7, 9, 0 ]

3,数组去重

const arr = [3, 1, 'a', 'b', 'c', 3, 1, 'b']

for (let i = 0; i < arr.length; i++) {
    
    
  for (let j = i + 1; j < arr.length; j++) {
    
    
    if (arr[i] === arr[j]) {
    
    
      arr.splice(j, 1)
      j--
    }
  }
}
console.log(arr) // [ 3, 1, 'a', 'b', 'c' ]

4,判断文件路径的后缀名是否合法。

const filename = 'D:packages\public\favicon.ico'
const extname = ['.jpg', '.png', '.gif', '.webp', '.ico']
const index = filename.lastIndexOf('.')
let ext = ''
if (index) {
    
    
  ext = filename.substring(index)
}

if (extname.includes(ext)) {
    
    
  console.log('合法')
}

猜你喜欢

转载自blog.csdn.net/qq_40147756/article/details/132534055