==
和 ===
的不同trim
方法,保证浏览器兼容性requestAnimateFrame
var 的变量提升:
// 变量提升 ES5
console.log(a) // undefined
var a = 200
等同于:
var a
console.log(a) // undefined
a = 200
如果使用let
,会是这样:
console.log(a) // ReferenceError: a is not defined
let a = 200
let 块级作用域:
// 块级作用域
for (let i = 0; i < 10; i++) {
let j = i + 1
}
console.log(j) // ReferenceError: j is not defined
undefined
string
number
boolean
symbol
object
(注意: typeof null === ‘object’)function
parseInt
parseFloat
toString
等if
逻辑运算
==
+拼接字符串
实现效果如下:
// 实现如下效果
const obj1 = {
a:10, b:{
x: 100, y: 200}}
const obj1 = {
a:10, b:{
x: 100, y: 200}}
isEqual(obj1, obj2) === true
// 判断是否是对象或数组
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. 先取出 obj1 和 obj2 的 keys ,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 2. 以 obj1 为基准,和 obj2 一次递归比较
for (let key in obj1) {
// 比较当前 key 的 val —— 递归!!!
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
// 3. 全相等
return true
}
// 测试
const obj1 = {
a: 100,
b: {
x: 100,
y: 200
}
}
const obj2 = {
a: 100,
b: {
x: 100,
y: 200
}
}
// console.log( obj1 === obj2 )
console.log( isEqual(obj1, obj2) )
const arr1 = [1, 2, 3]
const arr2 = [1, 2, 3, 4]
'1-2-3'.split('-') // [1,2,3]
[1,2,3].join('-') // '1-2-3'
功能是什么?
返回值是什么?
是否会对原数组造成影响?
const arr = [10, 20, 30, 40]
// pop
const popRes = arr.pop()
console.log(popRes, arr) // 40 [10,20,30]
// shift
const shiftRes = arr.shift()
console.log(shiftRes, arr) // 10 [20,30,40]
// push
const pushRes = arr.push(50) // 返回 length
console.log(pushRes, arr) // 5 [10,20,30,40,50]
// unshift
const unshiftRes = arr.unshift(5) // 返回 length
console.log(unshiftRes, arr) // 5 [5,10,20,30,40]
纯函数:不对外界产生副作用的函数就叫纯函数
concat
map
filter
slice
就是纯函数,它们具有以下特点:
const arr = [10, 20, 30, 40]
// concat
const arr1 = arr.concat([50, 60, 70])
console.log(arr) // [10, 20, 30, 40]
console.log(arr1) //[10, 20, 30, 40, 50, 60, 70]
// map
const arr2 = arr.map(num => num * 10)
console.log(arr) // [10, 20, 30, 40]
console.log(arr2) //[100, 200, 300, 400]
// filter
const arr3 = arr.filter(num => num > 25)
console.log(arr) // [10, 20, 30, 40]
console.log(arr3) //[30, 40]
// slice
const arr4 = arr.slice()
console.log(arr) // [10, 20, 30, 40]
console.log(arr4) //[10, 20, 30, 40]
非纯函数
push
pop
shift
unshift
forEach
some
every
reduce
const arr = [10, 20, 30, 40, 50]
const arr1 = arr.slice()
console.log(arr1) // [10, 20, 30, 40, 50]
const arr2 = arr.slice(1, 4)
console.log(arr2) // [20, 30, 40]
const arr3 = arr.slice(2)
console.log(arr3) // [30, 40, 50]
const arr4 = arr.slice(-3)
console.log(arr4) // [30, 40, 50]
const arr = [10, 20, 30, 40, 50]
// splice 非纯函数
const spliceRes = arr.splice(1, 2, 'a', 'b', 'c')
console.log(spliceRes, arr) //[20,30] [10, 'a', 'b', 'c', 40, 50]
const spliceRes1 = arr.splice(1, 2)
console.log(spliceRes1, arr)//[20,30] [10, 40, 50]
const spliceRes2 = arr.splice(1, 0, 'a', 'b', 'c')
console.log(spliceRes2, arr)//[] [10, "a", "b", "c", 20, 30, 40, 50]
const res = [10, 20, 30].map(parseInt)
console.log(res) // [10, NaN, NaN]
拆解后等同于如下:
[10, 20, 30].map((num, index) => {
return parseInt(num, index)
})
parseInt()
函数可解析一个字符串,并返回一个整数。
参数 | 描述 |
---|---|
string | 必需。要被解析的字符串。 |
radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。 |
如果string
不是一个字符串,就会将其转换为字符串(toString);
当参数 radix
的值为 0,或没有设置该参数时,parseInt()
会根据 string
来判断数字的基数;
parseInt(10, 0) // 10
parseInt(10, 10) // 10
parseInt(20,1) // NaN
parseInt(30, 2) // NaN
Ajax, jQuery ajax, fetch,axios ヾ(⌐■_■)ノ看完 你就明白了~~
fn.call(this, p1, p2, p3)
fn.apply(this, arguments)
var fn1 = fn.bind(this, p1, p2, p3)
fn1()
相同点:
改变对象的执行上下文(总的来说,就是改变this的指向)。
this关键字,就是所谓的执行上下文。this关键字在函数中,表示的是一个指向,this的指向永远是一个对象。
都可以指定调用实参。
不同点:
注意:
如果call()和apply()的第一个参数是null或者undefined,那么this的指向就是全局变量,在浏览器里就是window对象。
代码演示:
var num = "全局中的num";
var o1 = {
num:"o1中的num",
f1:function(){
console.log(this.num);
},
f2:function (a,b) {
console.log(a+b);
return a+b;
}
}
var o2 = {
num:"o2中的num"
}
o1.f1.call(o2); //输出:o2中的num
o1.f1.apply(o2);//输出:o2中的num
var f3 = o1.f1.bind(o2);
f3();//输出:o2中的num
o1.f2.call(o2,1,2); //输出:3
o1.f2.apply(o2,[1,2]);//输出:3
var f4 = o1.f2.bind(o2,1,2);
f4();//输出:3
利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行!
推荐:JS事件绑定 事件冒泡与捕获 事件代理
JS基础知识:作用域和闭包 ლ(´ڡ`ლ)一样的话题,一样的配方!
闭包影响:
变量会常驻内存,得不到释放。闭包不要乱用
DOM操作总结 (。♥ᴗ♥。) 哇!!
DOM操作总结 (。♥ᴗ♥。) 哇!!
同源策略和跨域 (ノ°▽°)ノ 冲鸭!征服她!!!
浏览器的同源策略(服务端没有同源策略)和跨域
后端如何nginx, 我们一般称为转发,不称为跨域
哪些html 标签能绕过跨域?
jsonp原理
简易代码示例:
后端被请求文件:jsonp.js (由后端处理拼接生成)
// jsonp.js 文件内容
abc(
{
name: 'xxx' }
)
前端请求代码:
<script>
window.abc = function (data) {
console.log(data)
}
</script>
<script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
window.addEventListener('load', function () {
// 页面的全部资源加载完才会执行,包括图片,视频等
})
document.addEventListener('DOMContentLoaded', function () {
// DOM 渲染完即可执行,此时图片,视频还可能没有加载完
})
==
和
===
的区别
JS基础知识:变量的类型和计算
==
会尝试类型转换===
严格相等==
?function fn() {...}
const fn = function() {...}
// 函数声明
const res = sum(10, 20)
console.log(res) // 30
function sum(x, y) {
return x + y
}
// 函数表达式
var res = sum(10, 20)
console.log(res) // 报错:sum is not a function
var sum = function (x, y) {
return x + y
}
前提:要先明白原型和原型链 JS基础知识:原型和原型链
Object.create({…}) : 创建一个空对象,把原型__proto__
指向传入的对象
代码示例:
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 obj21 = new Object(obj1) // obj21 === obj1
const obj3 = Object.create(null)
const obj4 = new Object() // {}
const obj5 = Object.create({
a: 10,
b: 20,
sum() {
return this.a + this.b
}
})
const obj6 = Object.create(obj1) // obj6.__proto__ == obj1
const User = {
count: 1,
getCount: function() {
return this.count
}
}
console.log(User.getCount()) // 1
const func = User.getCount
console.log(func()) // undefined
this 的指向:看它执行的时候
func
是在window
上下文中被执行的,func
中的this
指向的是window
,所以不会访问到count
属性
let i
for(i = 1; i<=3; i++){
setTimeout(function() {
console.log(i) // 4 4 4
}, 0)
}
const reg = /^[a-zA-Z]\w{5,29}$/
let a = 100
function test() {
console.log(a)
a = 10
console.log(a)
}
test()
console.log(a)
// 100 10 10
trim
方法,保证浏览器兼容性
作用:去除字符串开头和结尾的空字符串
// 原型,this,正则表达式
String.prototype.trim = function () {
return this.replace(/^\s+/,'').replace(/\s+$/,'')
}
方法一:
// 获取多个数字中的最大值
function max() {
const nums = Array.prototype.slice.call(arguments) // 变为数组
let max = 0
nums.forEach(n => {
if (n > max) {
max = n
}
})
return max
}
方法二:使用Math API
Math.max(10,20,46,5)
方式一:
try {
// todo
} catch (ex) {
console.error(ex) // 手动捕获 catch
} finally {
// todo
}
方式二:
// 自动捕获
window.onerror = function (message, source, lineNum, colNum, error) {
// 注意:
// 对于跨域的JS,如 CDN 的,不会有详细的报错信息
// 对于压缩的JS, 还要配合 sourceMap 反查到未压缩代码的行,列
}
window.JSON
是一个全局对象: JSON.stringify
:把对象转换成json字符串 JSON.parse
:把字符串转换成对象location.search
URLSearchParams
代码示例:
浏览器输入:http://127.0.0.1:8080/index.html?a=10&b=20&c=30
// 传统方式
function query(name) {
const search = location.search.substr(1) // 类似 array.slice(1)
// search: 'a=10&b=20&c=30'
const reg = new RegExp(`(^|&)${
name}=([^&]*)(&|$)`, 'i')
const res = search.match(reg) // search字符串来匹配reg表达式
if (res === null) {
return null
}
return res[2]
}
console.log(query('a')) // 10
// URLSearchParams
function query(name) {
const search = location.search
const p = new URLSearchParams(search)
return p.get(name)
}
console.log( query('b') ) // 20
方式一:
// 传统方式,分析 search
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
}
方式二:
// 使用URLSearchParams
function queryToObj() {
const res = {
}
const pList = new URLSearchParams(location.search)
pList.forEach((val, key) => {
res[key] = val
})
return res
}
flatern
, 考虑多层级
作用效果如下:
flat([[1,2], 3, [4, 5, [6, 7, [8, 9, [10, 11]]]]])
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
多层数组需要递归:
function flat(arr) {
// 验证 arr 中,还有没有深层数组 [1, 2, [3, 4]]
const isDeep = arr.some(item => item instanceof Array) // arr数组中只要有一个元素符合Array类型
if (!isDeep) {
return arr // 已经是 flatern [1, 2, 3, 4]
}
const res = Array.prototype.concat.apply([], arr)
return flat(res) // 递归
}
const res = flat( [1, 2, [3, 4, [10, 20, [100, 200]]], 5] )
console.log(res) // [1, 2, 3, 4, 10, 20, 100, 200, 5]
方式一:
// 传统方式
function unique(arr) {
const res = []
arr.forEach(item => {
if (res.indexOf(item) < 0) {
res.push(item)
}
})
return res
}
const res = unique([30, 10, 20, 30, 40, 10])
console.log(res) //[30, 10, 20, 40]
方式二:
// 使用 Set (无序,不能重复)
function unique(arr) {
const set = new Set(arr)
return [...set]
}
const res = unique([30, 10, 20, 30, 40, 10])
console.log(res) //[30, 10, 20, 40]
建议: 能使用Set就使用Set。 Set比较快,传统方式需要循环。兼容性和速度看需求。
JS基础知识:变量的类型和计算
注意: Object.assign
不是深拷贝!只拷贝了一层
/**
* 深拷贝
*/
const obj1 = {
age: 20,
name: 'xxx',
address: {
city: 'beijing'
},
arr: ['a', 'b', 'c']
}
const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
obj2.arr[0] = 'a1'
console.log(obj1.address.city)
console.log(obj1.arr[0])
/**
* 深拷贝
* @param {Object} obj 要拷贝的对象
*/
function deepClone(obj = {
}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是对象和数组,直接返回
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) {
result = []
} else {
result = {
}
}
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
requestAnimationFrame
setTimeout
要手动控制频率,而 RAF 浏览器会自动控制iframe
中,RAF会暂停,而 setTimeout
依然执行代码演示:
html 部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JS 真题演示</title>
<style>
#div1 {
width: 100px;
height: 50px;
background-color: red;
}
</style>
</head>
<body>
<p>JS 真题演示</p>
<div id="div1"></div>
<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.js"></script>
<script src="./RAF.js"></script>
</body>
</html>
JS部分:
方式一:setTimeout
// 3s 把宽度从 100px 变为 640px ,即增加 540px
// 60帧/s ,3s 180 帧 ,每次变化 3px
const $div1 = $('#div1')
let curWidth = 100
const maxWidth = 640
// setTimeout
function animate() {
curWidth = curWidth + 3
$div1.css('width', curWidth)
if (curWidth < maxWidth) {
setTimeout(animate, 16.7) // 自己控制时间
}
}
animate()
方式二:RAF
// 3s 把宽度从 100px 变为 640px ,即增加 540px
// 60帧/s ,3s 180 帧 ,每次变化 3px
const $div1 = $('#div1')
let curWidth = 100
const maxWidth = 640
// RAF
function animate() {
curWidth = curWidth + 3
$div1.css('width', curWidth)
if (curWidth < maxWidth) {
window.requestAnimationFrame(animate) // 时间不用自己控制
}
}
animate()
【前端性能优化】 不懂优化的前端 不是好前端…
【前端性能优化】JS优化,难么?
HTML和CSS代码结构优化
静态资源优化:图片优化
下一篇:前端JS初级面试题二 (。•ˇ‸ˇ•。)老铁们!快来瞧瞧自己都会了么