Hellom's Studio.

JS基础知识练习题

字数统计: 2.7k阅读时长: 13 min
2020/03/05 Share

变量类型及计算

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数组去重

  • 使用 嵌套 for 循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 遇到相等删除 原数组递减1
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)
  • 使用 indexOf () 方法
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, 23, 44, 5, 3, 9]
  • 使用 includes 方法
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, 23, 44, 5, 3, 9]
  • 使用 ES6 提供的数据结构 Set
1
2
3
4
5
6
7
8
9
10
11
12
/**
* Array.from => 将两类对象转换为真正的数组 :类似数组的对象(array-like object) 和 可遍历(iterable)的对象
*
* Set => 类似于数组 但是成员都是唯一的 没有重复的值
*/
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];

//第一种 instanceof
arr instanceof Array

//第二种 原型
Object.prototype.toString.call(arr)

//第三种 es6新增
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; //true
Student.prototype.__proto__ === People.prototype; //true
People.prototype.__proto__ === Object.prototype; //true
People.prototype.__proto__.__proto__; //null

作用域和闭包

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);

// 获取数组中第一个值 也就是this的值
const t = args.shift();

// fn1.bind(...) 中的 fn1
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. 实际开发中闭包的应用场景,举例说明

  • 隐藏数据

  • 以下为简单的 cache 工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//闭包隐藏数据,只提供 API
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')); // 100

异步和单线程

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)


/*
输出结果以此为:
1
3
5
4
2
*/

2. 同步和异步的区别是什么?

  • 同步:

    • 会阻塞代码执行
  • 异步:

    • 不会阻塞代码执行

    • 基于 JS 单线程语言的特性

    • 是基于 callback 函数调用

    • 为了解决回调地狱(callback hell)的问题

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) // 140
return img1 // 普通对象
})
.then(img1 => {
console.log(img1.height) // 140
return loadImg(url2) // promise 实例
})
.then(img2 => {
console.log(img2.width) // 100
})
.catch(err => console.error(err))

4.前端使用异步的场景有哪些?

  • 网络请求,ajax调用,图片加载

  • 定时任务,setTimeout()

JS-Web-API-DOM

1. attr 和 property的区别

  • property:修改对象属性,不会体现到 html 结构中

  • attribute: 修改 html 属性,会作用到 html 结构中

  • 两者都有可能引起 DOM 重新渲染

1
2
3
4
5
6
7
8
9
10
11
const pList = document.querySelectorAll('p')

// property 形式
const p1 = pList[0]
p1.style.width = '100px'
p1.className = 'red'


// attribute
p1.setAttribute('data-name', 'test')
console.log(p1.getAttribute('data-name')); // test

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
// DOM 节点操作
const div1 = document.getElementById('div1') // 元素
const divList = document.getElementsByTagName('div') //集合
const containerList = document.getElementsByClassName('.container') // 集合
const pList = document.querySelectorAll('p') // 集合


// DOM 结构操作
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) // <div id="div2"></div>

// 获取子元素列表
const div1ChildNodes = div1.childNodes // 获取div1下的所有子节点
console.log(div1ChildNodes) // NodeList(7) [text, text, p, text, p, text, p]

/*
1. 转化为数组
2. 通过判断 nodeType 是否等于 1
标签的节点类型都是 1
从而过滤出 所有的 p 标签
*/
const div1ChildNodesP = Array.prototype.slice.call(div1ChildNodes)
.filter(node => {
if (node.nodeType === 1) {
return true
}
return false
})

console.log(div1ChildNodesP) // [p ,p ,p]

4. 一次性插入多个DOM节点 考虑性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const list = document.getElementById('list')

// 创建一个文档片段,此时还没插入到 DOM 树中
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);
}

//都完成之后,再一次性插入 DOM 树中
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) //https://coding.imooc.com/lesson/115.html?a=100&b=200#mid=30378

console.log(location.protocol) // https:

console.log(location.host) // coding.imooc.com

console.log(location.search) // ?a=100&b=200

console.log(location.hash) // #mid=30378

console.log(location.pathname) // /lesson/115.html

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. 描述事件冒泡的流程

  • 基于 DOM 树形结构

  • 事件会顺着触发元素向上冒泡 => p => div => body

  • 应用场景:代理

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)) // { name: zhangsan }
.catch(err => console.error(err))

2. 跨域的常用实现方式

  • JSONP

  • CORS

CATALOG
  1. 1. 变量类型及计算
    1. 1.1. 1. typeof 能判断哪些类型?
      1. 1.1.1. 考点:JS 变量类型
    2. 1.2. 2. 何时使用 === 何时使用 ==
      1. 1.2.1. 考点:强制类型转换
    3. 1.3. 3. 值类型和引用类型的区别是?
    4. 1.4. 4. 手写深拷贝
    5. 1.5. 5. js数组去重
  2. 2. 原型和原型链
    1. 2.1. 1.如何准确判断一个变量是不是一个数组?
    2. 2.2. 2.手写一个简易的jQuery 考虑插件和扩展性
    3. 2.3. 3. class的原型本质 怎么理解?
  3. 3. 作用域和闭包
    1. 3.1. 1. this 的不同应用场景,如何取值?
    2. 3.2. 2. 手写 bind 函数?
    3. 3.3. 3. 实际开发中闭包的应用场景,举例说明
  4. 4. 异步和单线程
    1. 4.1. 1. setTimeout 笔试题
    2. 4.2. 2. 同步和异步的区别是什么?
    3. 4.3. 3.手写用 promise 加载一张图片
    4. 4.4. 4.前端使用异步的场景有哪些?
  5. 5. JS-Web-API-DOM
    1. 5.1. 1. attr 和 property的区别
    2. 5.2. 2. DOM是哪种数据结构
    3. 5.3. 3. DOM操作的常用API
    4. 5.4. 4. 一次性插入多个DOM节点 考虑性能
  6. 6. JS-Web-API-BOM
    1. 6.1. 1. 如何识别浏览器的类型
    2. 6.2. 2.分析拆解 url 各个部分
  7. 7. JS-Web-API-事件
    1. 7.1. 1. 编写一个通用的事件监听函数
    2. 7.2. 2. 描述事件冒泡的流程
    3. 7.3. 3. 无限下拉的图片列表 如何监听每个图片的点击
  8. 8. JS-Web-API-Ajax
    1. 8.1. 1. 手写一个简易的ajax
    2. 8.2. 2. 跨域的常用实现方式