变量类型及计算
1. typeof 能判断哪些类型?
考点:JS 变量类型
1 2 3 4 5 6 7 8 9 10 11
| null undefined String Number Boolean Symbol
Array Object
|
2. 何时使用 === 何时使用 ==
考点:强制类型转换
除了 null
用 ==
以外,其他一律用 ===
==
: 非严格模式下使用 先转换在比较
===
: 严格模式下使用 仅比较不转换
3. 值类型和引用类型的区别是?
存储位置不同
复制方式不同
值类型的复制是深拷贝,变量值互不影响
引用类型的复制是浅拷贝,只是数据的引用
4. 手写深拷贝
注意判断是否是引用类型
注意判断是对象还是数组
递归调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) { return obj; }
let result;
if (obj instanceof Array) { result = []; } else { result = {}; }
for (let key in obj) { if (obj.hasOwnProperty(key)) { result[key] = deepClone(obj[key]) } }
return result;
}
const obj1 = { age: 18, name: 'xxx', address: { city: "shanghai" } }
const obj2 = deepClone(obj1); obj2.name = "ssss";
|
5. js数组去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const arrr = [1, 23, 44, 5, 3, 5, 3, 9, 1]
function unip(arr) { 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--; } } } } unip(arrr); console.log(arrr)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const arrr = [1, 23, 44, 5, 3, 5, 3, 9, 1]
function unip(arr) { let newArr = []
for (let i = 0; i < arr.length; i++) { if (newArr.indexOf(arr[i]) == -1) { newArr.push(arr[i]) } }
return newArr }
console.log(unip(arrr))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const arrr = [1, 23, 44, 5, 3, 5, 3, 9, 1]
function unip(arr) { let newArr = []
for (let i = 0; i < arr.length; i++) { if (!newArr.includes(arr[i])) { newArr.push(arr[i]) } }
return newArr }
console.log(unip(arrr))
|
1 2 3 4 5 6 7 8 9 10 11 12
|
const arrr = [1, 23, 44, 5, 3, 5, 3, 9, 1]
function unip(arr) { return Array.from(new Set(arr)) }
console.log(unip(arrr))
|
原型和原型链
1.如何准确判断一个变量是不是一个数组?
1 2 3 4 5 6 7 8 9 10
| var arr = [1,3,4,5];
arr instanceof Array
Object.prototype.toString.call(arr)
Array.isArray(arr)
|
2.手写一个简易的jQuery 考虑插件和扩展性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| class jQuery { constructor(selector) { const result = document.querySelectorAll(selector); const length = result.length; for (let i = 0; i < length; i++) { this[i] = result[i]; } this.length = length; this.selector = selector; }
get(index) { return this[index]; }
each(fn) { for (let i = 0; i < this.length; i++) { const elem = this[i]; fn(elem); } }
on(type, fn) { return this.each(elem => { elem.addEventListener(type, fn, false); }) } }
const $p = new jQuery('p'); $p.get(1); $p.each(e => console.log(e.nodeName));
jQuery.prototype.toString = function () { console.log('创建一个新方法') }
class myjQuery extends jQuery { constructor(selector) { super(selector); } style(data) { console.log(data); } }
|
3. class的原型本质 怎么理解?
class 的本质就原型 类型是函数 是 es6 的语法糖
每个实例对象都会有一个 隐式原型 __proto__
每个类都会有一个 显式原型 prototype
实例对象的__proto__ === 类的 prototype
=> true
每个 实例对象的__proto__
指向 类的 prototype
实例的属性和方法先从自身找,找不到再向上通过 __proto__
找
Object
是所有原型的最上层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class People { constructor(name, age) { this.name = name; this.age = age; }
eat() { console.log(`${this.name} eating.....`); } }
class Student extends People { constructor(name, age, number) { super(name, age); this.number = number; }
sayHi() { console.log(`姓名:${this.name},学号:${this.age}`); } }
class Teacher extends People { constructor(name, age, major) { super(name, age); this.major = major; }
teach() { console.log(`${this.name} 教 ${this.major}`); } }
const xialuo = new Student('夏洛', 18, 20); const wanglaoshi = new Teacher('王老师', 45, '语文');
xialuo.__proto__ === Student.prototype; Student.prototype.__proto__ === People.prototype; People.prototype.__proto__ === Object.prototype; People.prototype.__proto__.__proto__;
|
作用域和闭包
1. this 的不同应用场景,如何取值?
作为普通函数
使用 call apply bind
在 class 方法中被调用
箭头函数
作为对象方法被调用
重点:this的取值,是在函数执行的时候确定的。
2. 手写 bind 函数?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| Function.prototype.bind1 = function () {
const args = Array.prototype.slice.call(arguments);
const t = args.shift();
const self = this;
return function () { return self.apply(t, args); } }
function fn1(a, b, c) { console.log('this', this); console.log(a, b, c); return 'this is fn1'; }
const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30);
fn2();
|
3. 实际开发中闭包的应用场景,举例说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function createCache() { const data = {} return { set: function(key, value) { data[key] = value }, get: function(key) { return data[key] } } }
const c = new createCache() c.set('a', 100) console.log(c.get('a'));
|
异步和单线程
1. setTimeout 笔试题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| console.log(1);
setTimeout(function() { console.log(2) }, 1000)
console.log(3)
setTimeout(function() { console.log(4) }, 0)
console.log(5)
|
2. 同步和异步的区别是什么?
3.手写用 promise 加载一张图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function loadImg(url) { return new Promise((resolve, reject) => { const img = document.createElement('img') img.onload = () => { resolve(img) }, img.onerror = () => { const err = new Error(`图片加载失败 ${url}`) reject(err) } img.src = url }) }
const url1 = 'https://img4.sycdn.imooc.com/545863570001cf3702200220-140-140.jpg'
const url2 = 'https://img4.mukewang.com/545863570001cf3702200220-100-100.jpg'
loadImg(url1) .then(img1 => { console.log(img1.width) return img1 }) .then(img1 => { console.log(img1.height) return loadImg(url2) }) .then(img2 => { console.log(img2.width) }) .catch(err => console.error(err))
|
4.前端使用异步的场景有哪些?
网络请求,ajax调用,图片加载
定时任务,setTimeout()
JS-Web-API-DOM
1. attr 和 property的区别
1 2 3 4 5 6 7 8 9 10 11
| const pList = document.querySelectorAll('p')
const p1 = pList[0] p1.style.width = '100px' p1.className = 'red'
p1.setAttribute('data-name', 'test') console.log(p1.getAttribute('data-name'));
|
2. DOM是哪种数据结构
树 (DOM树)
3. DOM操作的常用API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| const div1 = document.getElementById('div1') const divList = document.getElementsByTagName('div') const containerList = document.getElementsByClassName('.container') const pList = document.querySelectorAll('p')
const div1 = document.getElementById('div1') const div2 = document.getElementById('div2')
const newP = document.createElement('p') newP.innerHTML = 'this is newP'
div1.appendChild(newP)
const p1 = document.getElementById('p1') div2.appendChild(p1)
console.log(p1.parentNode)
const div1ChildNodes = div1.childNodes console.log(div1ChildNodes)
const div1ChildNodesP = Array.prototype.slice.call(div1ChildNodes) .filter(node => { if (node.nodeType === 1) { return true } return false })
console.log(div1ChildNodesP)
|
4. 一次性插入多个DOM节点 考虑性能
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const list = document.getElementById('list')
const frag = document.createDocumentFragment();
for (let i = 0; i < 10; i++) { const li = document.createElement('li'); li.innerHTML = `this is item ${i}`; frag.appendChild(li); }
list.appendChild(frag);
|
JS-Web-API-BOM
1. 如何识别浏览器的类型
1
| const ua = navigator.userAgent;
|
2.分析拆解 url 各个部分
1 2 3 4 5 6 7 8 9 10 11
| console.log(location.href)
console.log(location.protocol)
console.log(location.host)
console.log(location.search)
console.log(location.hash)
console.log(location.pathname)
|
JS-Web-API-事件
1. 编写一个通用的事件监听函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| function bindEvent(elem, type, selector, fn) { if (fn == null) { fn = selector selector = null }
elem.addEventListener(type, e => { const target = e.target if (selector) { if (target.matches(selector)) { fn.call(target, event) } } else { fn.call(target, event) } }) }
const btn = document.getElementById('btn') bindEvent(btn, 'click', e => { console.log(btn.target.innerHTML); e.preventDefault() console.log('clicked!') })
const div3 = document.getElementById('div3') bindEvent(div3, 'click', 'a', e => { e.preventDefault() alert(e.target.innerHTML) })
|
2. 描述事件冒泡的流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <body> <button id="btn">按钮</button>
<div id="div1"> <p id="p1">激活</p> <p id="p2">取消</p> <p id="p3">取消</p> <p id="p3">取消</p> </div>
<div id="div2"> <p id="p5">取消</p> <p id="p6">取消</p> </div>
</body>
|
1 2 3 4 5 6 7 8 9 10
| const p1 = document.getElementById('p1') bindEvent(p1, 'click', e => { e.stopPropagation(); console.log('激活') })
const body = document.body bindEvent(body, 'click', e => { console.log('取消') })
|
3. 无限下拉的图片列表 如何监听每个图片的点击
使用事件代理
用 e.target
获取触发元素
再用 matches
来判断是否是触发元素
1 2 3 4 5 6
| <div id="div3"> <a href="#">a1</a> <a href="#">a2</a> <a href="#">a3</a> <button>加载更多...</button> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function bindEvent(elem, type, selector, fn) { if (fn == null) { fn = selector selector = null }
elem.addEventListener(type, e => { const target = e.target if (selector) { if (target.matches(selector)) { fn.call(target, event) } } else { fn.call(target, event) } }) }
const div3 = document.getElementById('div3') bindEvent(div3, 'click', 'a', e => { e.preventDefault() alert(e.target.innerHTML) })
|
JS-Web-API-Ajax
1. 手写一个简易的ajax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function ajax(url) { const p = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { resolve(JSON.parse(xhr.responseText)) } else if (xhr.status === 404) { reject(new Error('404 not found')) } } } xhr.send(null) })
return p }
const url = 'test.json';
ajax(url) .then(res => console.log(res)) .catch(err => console.error(err))
|
2. 跨域的常用实现方式