面接の質問
var と let const の違い
- var は ES5 以前の構文、const は ES6 の構文とします。
- var と let は変数であり変更可能ですが、const は定数で変更できません
- var には変数のホイスティングがありますが、const にはありません
- var にはブロック スコープがありません。const にはブロック スコープがあります (ES6 構文にはブロック スコープがあります)。
// 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)
typeof の戻り値の型は何ですか?
// 判断所有值类型
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'
キャストと暗黙的な変換を列挙する
- 必須
parseInt
parseFloat
- 文字列を連結するための暗黙的な
if
、==
、+
lodash isEqual などの手書きの深さの比較
// 实现如下效果
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
}
split()
のjoin()
違い
'1-2-3'.split('-')
[1,2,3].join('-')
配列はそれぞれpop
push
unshift
shift
何をするのか
以下の点に注意してください
- 機能は何をするのでしょうか?
- 戻り値は何ですか?
- 元の配列に影響はありますか?
- 元の配列に影響を与えないようにするにはどうすればよいですか?
concat
slice
map
filter
【拡張】配列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()
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(parseInt)
結果は何ですか?
// 拆解开就是
[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)
})
Ajaxリクエストでのgetとpostの違い
- 通常、get はクエリ操作に使用され、post は通常、送信操作に使用されます。
- get パラメータは URL にあり、投稿はリクエスト本文にあります
- セキュリティ: 投稿リクエストは CSRF を防ぐのが簡単です
(郵便コードのデモ: Web ページ、郵便名)
コールと適用の違い
fn.call(this, p1, p2, p3)
fn.apply(this, arguments)
イベントデリゲーション(プロキシ)とは
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()
DOM ノードを移動する削除置換挿入メソッドを追加
(確認のためにコード スニペットをここに貼り付けます)
DOM 操作を減らすにはどうすればよいでしょうか?
- DOM クエリ結果をキャッシュする
- 複数の操作を 1 つの挿入にマージ
(確認のためにコード スニペットをここに貼り付けます)
jsonpの原理とそれが本物のajaxではない理由を説明する
- ブラウザの同一オリジンポリシー、クロスドメインとは何ですか?
- クロスドメインをバイパスできる HTML タグはどれですか?
- jsonpの原則
ドキュメントのロードとドキュメントの準備の違い
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
}
new Object()
のObject.create()
違い
例 1
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
}
})
// 分别打印看结构
例 2
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?
文字列が文字で始まり、数字、アンダースコア、文字が続き、長さが 6 ~ 30 であると判断します。
const reg = /^[a-zA-Z]\w{5,29}$/
- 正規表現ルールを参照してください https://www.runoob.com/regexp/regexp-syntax.html
- 一般的な正規表現を表示する
/\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+$/,"")
}
知識ポイント:プロトタイプ、コレ、レギュラー
複数の値の中から最大値を取得するにはどうすればよいですか?
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
}
JS で継承を実現するにはどうすればよいですか?
クラスコード
プログラム内で例外をキャッチする方法
try {
// todo
} catch (ex) {
console.error(ex) // 手动捕获 catch
} finally {
// todo
}
// 自动捕获 catch(但对跨域的 js 如 CDN 的,不会有详细的报错信息)
window.onerror = function (message, source, lineNom, colNom, error) {
}
JSONとは何ですか?
まず第一に、json はデータ形式の標準であり、言語やプラットフォームに依存しない本質的に文字列です。json 内の文字列は二重引用符で囲む必要があることに注意してください。
{
"name": "张三",
"info": {
"single": true,
"age": 30,
"city": "北京"
},
"like": ["篮球", "音乐"]
}
次に、JSON は js の組み込みグローバル変数であり、 2 つの APIJSON.parse
と を備えていますJSON.stringify
。
現在のページの URL パラメータを取得する
自分で気づく
// 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') )
新しいAPIURLSearchParams
const pList = new URLSearchParams(location.search)
pList.get('a')
URLパラメータをJSオブジェクトに解析しますか?
自分で書いてください
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
}
新しいAPIURLSearchParams
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セットを使用する
// 数组去重
function unique(arr) {
const set = new Set(arr)
return [...set]
}
unique([30, 10, 20, 30, 40, 10])
手書きのディープコピー
コードを貼り付けます
[注意]Object.assign
ディープコピーではありません、ちなみに使用方法については話せます
Object.assign(obj1, {...})
const obj2 = Object.assign({}, obj1, {...})
RAF requestAnimationFrame の導入
JS を使用してアニメーションを実現したい場合、古い方法は setTimeout を使用してリアルタイムで更新することですが、これは非常に非効率であり、遅延が発生する可能性があります。
- スムーズなアニメーションが必要な場合、更新頻度は 60 フレーム/秒、つまり 16.6 ミリ秒ごとにビューが更新されます。遅すぎると肉眼では引っかかりを感じますし、速すぎると肉眼では感じられず、リソースが無駄になります。
- setTimeoutを使用する場合はこの頻度を自分で制御する必要がありますが、requestAnimationFrameは自分で制御する必要はなく、ブラウザが自動的に制御します。
- バックグラウンド タブまたは非表示のタブでも
<iframe>
、setTimeout は引き続き実行され、requestAnimationFrame はコンピューティング リソースを節約するために自動的に一時停止されます。
(コードデモ)
フロントエンドのパフォーマンスの最適化について何を知っていますか? 一般に、どの側面を最適化する必要がありますか?
原則として
- より多くのメモリ、キャッシュ、またはその他の方法を使用する
- CPU の計算量が減り、ネットワークの量も減ります
方向
- ページと静的リソースをロードする
- ページのレンダリング