// var 变量提升 console.log('a', a) var a = 100 // let 没有变量提升 console.log('b', b) let b = 200
// var 没有块级作用域 for (var i = 0; i < 10; i++) { var j = 1 + i } console.log(i, j) // let 有块级作用域 for (let x = 0; x < 10; x++) { let y = 1 + x } console.log(x, y)
// 判断所有值类型 let a console.log(a) // 'undefined' const str = 'abc' typeof str // 'string' const n = 100 typeof n // 'number' const b = true typeof b // 'boolean' const s = Symbol('s') typeof s // 'symbol'
// 实现如下效果 const obj1 = {a: 10, b: { x: 100, y: 200 }} const obj2 = {a: 10, b: { x: 100, y: 200 }} isEqual(obj1, obj2) === true
// 判断是否是 object function isObject(obj) { return typeof obj === 'object' && obj !== null } // 全相等 function isEqual(obj1, obj2) { if (!isObject(obj1) || !isObject(obj2)) { // 值类型,不是对象或数组(注意,equal 时一般不会有函数,这里忽略) return obj1 === obj2 } if (obj1 === obj2) { // 两个引用类型全相等(同一个地址) return true } // 两个都是引用类型,不全相等 // 1. 先取出 obje2 obj2 的 keys,比较个数 const obj1Keys = Object.keys(obj1) const obj2Keys = Object.keys(obj2) if (obj1Keys.length !== obj2Keys.length) { // keys 个数不相等,则不是全等 return false } // 2. 以 obj1 为基准,和 obj2 依次递归比较 for (let key in obj1) { // 递归比较 const res = isEqual(obj1[key], obj2[key]) if (!res) { // 遇到一个不相等的,则直接返回 false return false } } // 3. 都相等,则返回 true return true }
'1-2-3'.split('-') [1,2,3].join('-')
注意以下几点
【扩展】数组 API 的纯函数和非纯函数
纯函数 —— 1. 不改变来源的数组; 2. 返回一个数组
const arr = [100, 200, 300] const arr1 = arr.concat([400, 500]) const arr2 = arr.map(num => num * 10) const arr3 = arr.filter(num => num > 100) const arr4 = arr.slice(-1)
非纯函数
情况1,改变了原数组
情况2,未返回数组
slice - 切片;splice - 剪接;
// slice() const arr1 = [10, 20, 30, 40, 50] const arr2 = arr1.slice() // arr2 和 arr1 不是一个地址,纯函数,重要!!! // arr.slice(start, end) const arr1 = [10, 20, 30, 40, 50] const arr2 = arr1.slice(1, 4) // [20, 30, 40] // arr.slice(start) const arr1 = [10, 20, 30, 40, 50] const arr2 = arr1.slice(2) // [30, 40, 50] // 负值 const arr1 = [10, 20, 30, 40, 50] const arr2 = arr1.slice(-2) // [40, 50]
// arr.splice(index, howmany, item1, ....., itemX) const arr1 = [10, 20, 30, 40, 50] const arr2 = arr1.splice(1, 2, 'a', 'b', 'c') // [20, 30] // arr1 会被修改,不是纯函数,即有副作用
// 拆解开就是 [10, 20, 30].map((num, index) => { return parseInt(num, index) // parseInt 第二个参数是进制 // 如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。 // 如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN })
// 可以对比 [10, 20, 30].map((num, index) => { return parseInt(num, 10) })
(post 代码演示:网页,postname)
const p1 = document.getElementById('p1') const body = document.body bindEvent(p1, 'click', e => { e.stopPropagation() // 注释掉这一行,来体会事件冒泡 alert('激活') }) bindEvent(body, 'click', e => { alert('取消') })
对页面的影响
// 自由变量示例 —— 内存会被释放 let a = 0 function fn1() { let a1 = 100 function fn2() { let a2 = 200 function fn3() { let a3 = 300 return a + a1 + a2 + a3 } fn3() } fn2() } fn1()
// 闭包 函数作为返回值 —— 内存不会被释放 function create() { let a = 100 return function () { console.log(a) } } let fn = create() let a = 200 fn() // 100 // 闭包 函数作为参数 —— 内存不会被释放 function print(fn) { let a = 200 fn() } let a = 100 function fn() { console.log(a) } print(fn) // 100
event.stopPropagation()
event.preventDefault()
window.addEventListener('load', function () { // 页面的全部资源加载完才会执行,包括图片、视频等 }) document.addEventListener('DOMContentLoaded', function () { // DOM 渲染完即可执行,此时图片、视频还可能没有加载完 })
const res = sum(10, 20) console.log(res) // 30 // 函数声明 function sum(x, y) { return x + y }
const res = sum(100, 200) console.log(res) // 报错!!! // 函数表达式 const sum = function(x, y) { return x + y }
示例一
const obj1 = { a: 10, b: 20, sum() { return this.a + this.b } } const obj2 = new Object({ a: 10, b: 20, sum() { return this.a + this.b } }) const obj3 = Object.create({ a: 10, b: 20, sum() { return this.a + this.b } }) // 分别打印看结构
示例二
const obj1 = { a: 10, b: 20, sum() { return this.a + this.b } } const obj2 = new Object(obj1) console.log(obj1 === obj2) // true const obj3 = Object.create(obj1) console.log(obj1 === obj3) // false const obj4 = Object.create(obj1) console.log(obj3 === obj4) // false // 然后修改 obj1 ,看 obj2 obj3 和 obj4 obj1.printA = function () { console.log(this.a) }
const User = { count: 1, getCount: function() { return this.count } } console.log(User.getCount()) // what? const func = User.getCount console.log( func() ) // what?
let i for(i = 1; i <= 3; i++) { setTimeout(function(){ console.log(i) }, 0) } // what?
const reg = /^[a-zA-Z]\w{5,29}$/
/\d{6}/ // 邮政编码 /^[a-z]+$/ // 小写英文字母 /^[A-Za-z]+$/ // 英文字母 /^\d{4}-\d{1,2}-\d{1,2}$/ // 日期格式 /^[a-zA-Z]\w{5,17}$/ // 用户名(字母开头,字母数字下划线,5-17位) /\d+\.\d+\.\d+\.\d+/ // 简单的 IP 地址格式
let a = 100 function test() { alert(a) a = 10 alert(a) } test() alert(a) // what?
String.prototype.trim= function (){ return this.replace(/^\s+/,"").replace(/\s+$/,"") }
知识点:原型,this,正则
Math.max(10, 30, 20, 40) // 以及 Math.min
function max() { const nums = Array.prototype.slice.call(arguments) // 变为数组 let max = 0 nums.forEach(n => { if (n > max) { max = n } }) return max }
class 代码
try { // todo } catch (ex) { console.error(ex) // 手动捕获 catch } finally { // todo }
// 自动捕获 catch(但对跨域的 js 如 CDN 的,不会有详细的报错信息) window.onerror = function (message, source, lineNom, colNom, error) { }
首先,json 是一种数据格式标准,本质是一段字符串,独立于任何语言和平台。注意,json 中的字符串都必须用双引号。
{ "name": "张三", "info": { "single": true, "age": 30, "city": "北京" }, "like": ["篮球", "音乐"] }
其次,JSON 是 js 中一个内置的全局变量,有 JSON.parse 和 JSON.stringify 两个 API 。
自己实现
// const url = 'https://www.xxx.com/path/index.html?a=100&b=200&c=300#anchor' function query(name) { const search = location.search.substr(1) // 去掉前面的 `?` const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i') const res = search.match(reg) if (res === null) { return null } return decodeURIComponent(res[2]) } console.log( query('a') ) console.log( query('c') )
新的 API URLSearchParams
const pList = new URLSearchParams(location.search) pList.get('a')
自己编写
function queryToObj() { const res = {} const search = location.search.substr(1) // 去掉前面的 `?` search.split('&').forEach(paramStr => { const arr = paramStr.split('=') const key = arr[0] const val = arr[1] res[key] = val }) return res }
新的 API URLSearchParams
function queryToObj() { const res = {} const pList = new URLSearchParams(location.search) pList.forEach((val, key) => { res[key] = val }) return res }
function flat(arr) { // 验证 arr 中,还有没有深层数组,如 [1, [2, 3], 4] const isDeep = arr.some(item => item instanceof Array) if (!isDeep) { return arr // 没有深层的,则返回 } // 多深层的,则 concat 拼接 const res = Array.prototype.concat.apply([], arr) // 回归上文,apply 和 call 的区别 return flat(res) // 递归调用,考虑多层 } flat([[1,2], 3, [4,5, [6,7, [8, 9, [10, 11]]]]])
要考虑:
ES5 语法手写。
// 写法一 function unique(arr) { const obj = {} arr.forEach(item => { obj[item] = 1 // 用 Object ,去重计算高效,但顺序不能保证。以及,非字符串会被转换为字符串!!! }) return Object.keys(obj) } unique([30, 10, 20, 30, 40, 10])
// 写法二 function unique(arr) { const res = [] arr.forEach(item => { if (res.indexOf(item) < 0) { // 用数组,每次都得判断是否重复(低效),但能保证顺序 res.push(item) } }) return res } unique([30, 10, 20, 30, 40, 10])
用 ES6 Set
// 数组去重 function unique(arr) { const set = new Set(arr) return [...set] } unique([30, 10, 20, 30, 40, 10])
粘贴代码
【注意】Object.assign 不是深拷贝,可以顺带讲一下用法
想用 JS 去实现动画,老旧的方式是用 setTimeout 实时刷新,但这样效率非常低,而且可能会出现卡顿。
上一篇:css实现文字大小自适应