js牛客刷题
# 牛客刷题
# js1直角三角形
请补全JavaScript代码,要求在页面上渲染出一个直角三角形,三角形换行要求使用"br"实现。三角形如下: * **
var triangle = document.querySelector('.triangle');
// 补全代码
let str = '';
for(let i = 0; i < 3; i++) {
let s = '';
for(let j = 0; j <= i; j++) {
s += '*';
}
str += (s + '<br/>')
}
triangle.innerHTML = str;
2
3
4
5
6
7
8
9
10
11
# js2文件扩展名
请补全JavaScript代码,要求以字符串的形式返回文件名扩展名,文件名参数为"filename"。
const _getExFilename = (filename) => {
// 补全代码
// return '.' + filename.split('.').pop()
let lastIndex = filename.lastIndexOf('.')
return lastIndex !== -1 ? filename.substring(lastIndex) : ''
}
console.log(_getExFilename('problem.xml'))
2
3
4
5
6
7
8
# js3 分隔符
描述
请补全JavaScript代码,要求返回参数数字的千分位分隔符字符串。
示例1
输入:
_comma(12300)
输出:
'12,300'
function _comma(number) {
// 补全代码
if(!String.prototype.splice) {
String.prototype.splice = function(start, delCount, newSubStr) {
return this.slice(0, start) + newSubStr + this.slice(start + Math.abs(delCount))
}
}
String.prototype.splice = function(start, delCount, newSubStr) {
return this.slice(0, start) + newSubStr + this.slice(start + Math.abs(delCount))
}
let str = number + ""
let n = str.length
if (str.charAt(0) == '-') {
n--
}
if (n <= 3) {
return str
} else {
let k = 3;
for(let i = 0; i < n / 3 - 1; i++) {
str = str.splice(-k, 0, ',')
k++;
k+=3
}
return str
}
}
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
正则表达式
// 利用正则表达式来解决
function _comma2(number) {
let str = number.toString()
// (\d)是第一个捕获组,用于匹配一个数字
// (?:\d{3})+$) 是一个非捕获组,用于匹配连续的三个数字字符
// (?=...)是一个零宽度正向先行断言,表示匹配一个位置,这个位置后面满足断言中指定的条件。
// $1 第一个捕获组的内容
return str.replace(/(\d)(?=(?:\d{3})+$)/g,'$1,')
}
2
3
4
5
6
7
8
9
function _comma3(number) {
let arr = Math.abs(number).toString().split('').reverse();
let i = 3
while(i < arr.length) {
arr.splice(i, 0, ',')
i+=4
}
let str = arr.reverse().join('')
return number < 0 ? '-' + str : str
}
2
3
4
5
6
7
8
9
10
11
# js4单向绑定
请补全JavaScript代码,要求每当id为"input"的输入框值发生改变时触发id为"span"的标签内容同步改变。 注意:
- 必须使用DOM0级标准事件(onchange)
// 补全代码
// (不要用const 还有let)
var input = document.querySelector('#input')
input.onchange = function () {
document.querySelector('#span').innerText = this.value
}
2
3
4
5
6
# 知识点
事件 | 功能 |
---|---|
onfocus (常用) | input标签获取焦点事件 |
onblur (常用) | input失去焦点事件(触发条件:先获取焦点,再失去焦点触发) |
onchange | input失去焦点并且它的value值发生变化时触发 |
oninput | input框输入过程中value值改变时实时触发,换句话说就是 每输入一个字符都会触发 |
onclick | input标签type="button"时的点击事件 |
onkeydown | input框输入时键盘按钮按下事件 |
onkeyup | input框输入时键盘按钮抬起事件,触发onkeyup事件之前一定触发onkeydown事件 |
onselect | input标签内容选中时触发事件 |
在元素内添加内容
文本内容 innerText
span.innerText = '追加的内容';
HTML内容 innerHTML
span.innerHTML = '<h3>追加内容为H3标签</h3>';
# js5创建数组
请补全JavaScript代码,要求返回一个长度为参数值并且每一项值都为参数值的数组。 注意:
- 请勿直接使用for/while
const _createArray = (number) => {
// 补全代码
return new Array(number).fill(number)
}
2
3
4
# js6版本判断
请补全JavaScript代码,该函数接收两个参数分别为旧版本、新版本,当新版本高于旧版本时表明需要更新,返回true,否则返回false。 注意:
- 版本号格式均为"X.X.X".
- X∈[0,9]
- 当两个版本号相同时,不需要更新
const _shouldUpdate = (oldVersion, newVersion) => {
// 补全代码
const old_version = oldVersion.split('.')
const new_version = newVersion.split('.')
if (old_version.length === new_version.length) {
for(let i = 0; i < old_version.length; i++) {
if (new_version[i] > old_version[i]) {
return true
}
}
} else {
return new_version.length > old_version.length
}
return false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ES6扩展运算符比较
// ES6扩展运算符比较
const _shouldUpdate2 = (oldVersion, newVersion) => {
// 补全代码
const old_version = oldVersion.split('.')
const new_version = newVersion.split('.')
return new_version > old_version
}
2
3
4
5
6
7
或者转为字符串比较
// 转为字符串比较
const _shouldUpdate3 = (oldVersion, newVersion) => {
// 补全代码
const old_version = oldVersion.split('.').join('')
const new_version = newVersion.split('.').join('')
return new_version > old_version
}
2
3
4
5
6
7
# js7无重复数组
请补全JavaScript代码,实现一个函数,要求如下:
根据输入的数字范围[start,end]和随机数个数"n"生成随机数
生成的随机数存储到数组中,返回该数组
返回的数组不能有相同元素 注意:
不需要考虑"n"大于数字范围的情况
输入:
getUniqueNums(2,10,4)
输出:
[4,6,2,8]
const _getUniqueNums = (start,end,n) => {
// 补全代码
let arr = []
let i = 0
while(i < n) {
let num = Math.floor(Math.random()*(end + 1 - start)) + start
if (!arr.includes(num)) {
arr.push(num)
i++
}
}
return arr
}
2
3
4
5
6
7
8
9
10
11
12
13
利用set
const _getUniqueNums2 = (start,end,n) => {
// 补全代码
let arr = new Set()
while(arr.size < n) {
let num = Math.floor(Math.random()*(end + 1 - start)) + start
arr.add(num)
}
return [...arr]
}
2
3
4
5
6
7
8
9
# 生成随机数
Math.random()
返回一个介于 0 ~ 1 之间的伪随机数(包括 0,不包括 1) [0,1)
Math.random()*(m-n)+n
生成 [ n, m )
范围内的随机数(大于等于n,小于m)
Math.floor(Math.random()*n)+1
生成 [ 1, n ] 范围内的随机整数(大于等于1,小于等于n)
Math.floor(Math.random()*(max-min+1))+min
生成 [ min, max ] 范围内的随机整数(大于等于min,小于等于max)
# 数组中查找指定元素
includes()
方法用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回 false。
第一个参数是‘需要查找的元素值’,第二个参数是‘从哪个索引处开始查找’,第二个参数如果为负数,则会按升序从 array.length + fromIndex 的索引开始查找
var a = [1,2,3,4,5,6]
a.includes(2) // true
a.includes(2,3) // false
2
3
4
indexOf()
方法返回指定元素在数组中的第一个索引,如果不存在,则返回-1。
该方法支持两个参数searchElement,fromIndex (可选),第一个参数是‘要查找的元素’,第二个参数是‘开始查找的索引位置’,如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。
var array = [2, 5, 9];
array.indexOf(2); // 0
array.indexOf(7); // -1
array.indexOf(9, 2); // 2
array.indexOf(2, -1); // -1
2
3
4
5
6
lastIndexOf()
方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。
var array = [2, 5, 9, 2];
array.lastIndexOf(2); // 3
array.lastIndexOf(7); // -1
array.lastIndexOf(2, 3); // 3
2
3
4
5
some()
方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。
语法:arr.some(callback(element[, index[, array]])[, thisArg])
「参数」:
callback:用来测试每个元素的函数,接受三个参数:
- element 数组中正在处理的元素。
- index 可选,数组中正在处理的元素的索引值。
- array 可选,被遍历的数组本身。
thisArg:可选,执行 callback 时使用的 this 值。
function isBiggerThan10(element, index, array) {
return element > 10;
}
[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
2
3
4
5
6
7
every()
方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
该方法的参数与 Array.prototype.some() 方法一样,这里就不再介绍了。它们两不同的是,some()只要数组中有一个元素满足条件就为真,every()要全部满足条件才为真。
function isBigEnough(element, index, array) {
return element >= 10;
}
[12, 5, 8, 130, 44].every(isBigEnough); // false
[12, 54, 18, 130, 44].every(isBigEnough); // true
2
3
4
5
filter()
方法创建一个新数组, 包含通过所提供函数实现的测试的所有元素。
该方法的参数与 some(),every()相同,callback 用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留
function isBigEnough(element) {
return element >= 10;
}
var filtered = [12, 5, 8, 130, 35].filter(isBigEnough);
2
3
4
5
find()
方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
该方法的参数与 some(),every(),filter()相同。
var inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'orange', quantity: 5}
];
function findOranges(fruit) {
return fruit.name === 'orange';
}
console.log(inventory.find(findOrange));
// { name: 'orange', quantity: 5 }
2
3
4
5
6
7
8
9
10
11
12
13
findIndex()
方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
该方法参数与 find() 相同,只是一个返回该元素,一个返回该元素在数组中的索引值。
var inventory = [
{name: 'apple', quantity: 2},
{name: 'banana', quantity: 0},
{name: 'orange', quantity: 5}
];
function findOrange(fruit) {
return fruit.name === 'orange';
}
console.log(inventory.findIndex(findOrange));
// { name: 'orange', quantity: 5 }
2
3
4
5
6
7
8
9
10
11
12
13
# 总结
方法名 | 参数 | 描述 | 返回值 |
---|---|---|---|
includes | searchElement,fromIndex | 判断数组中是否包含指定的值 | 布尔值 |
indexOf | searchElement,fromIndex | 查找元素在数组中首次出现的索引值 | 索引值,或者-1 |
lastIndexOf | searchElement,fromIndex | 查找元素在数组中最后一次出现的索引值 | 索引值,或者-1 |
some | callback[, thisArg] | 判断数组中是否有符合条件的元素 | 布尔值 |
every | callback[, thisArg] | 判断数组中是否每个元素都符合条件 | 布尔值 |
filter | callback[, thisArg] | 返回符合条件的所有元素组成的数组 | 数组 |
find | callback[, thisArg] | 返回数组中符合条件的第一个元素 | 数组中的元素,或者undefined |
findIndex | callback[, thisArg] | 返回符合条件的第一个元素的索引 | 索引值,或者-1 |
# js8数组排序
请补全JavaScript代码,根据预设代码中的数组,实现以下功能:
- 列表只展示数组中的name属性
- 实现点击"销量升序"按钮,列表内容按照销量升序重新渲染
- 实现点击"销量降序"按钮,列表内容按照销量降序重新渲染
注意:
- 必须使用DOM0级标准事件(onclick)
var cups = [
{ type: 1, price: 100, color: 'black', sales: 3000, name: '牛客logo马克杯' },
{ type: 2, price: 40, color: 'blue', sales: 1000, name: '无盖星空杯' },
{ type: 4, price: 60, color: 'green', sales: 200, name: '老式茶杯' },
{ type: 3, price: 50, color: 'green', sales: 600, name: '欧式印花杯' }
]
var ul = document.querySelector('ul');
var upbtn = document.querySelector('.up');
var downbtn = document.querySelector('.down');
// 补全代码
generateLi()
function generateLi() {
ul.innerHTML = ''
const names = cups.map(item => item.name)
names.forEach(item => {
var li = document.createElement('li')
li.innerText = item
ul.appendChild(li)
})
}
upbtn.onclick = function() {
sortBySales(1)
generateLi()
}
downbtn.onclick = function() {
sortBySales(2)
generateLi()
}
function sortBySales(flag) {
if (flag === 1) { // 升序
cups.sort((a,b) => {
return a.sales - b.sales
})
}else if(flag === 2) {
cups.sort((a,b) => { // 降序
return b.sales - a.sales
})
}
}
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
# js9新数组
请补全JavaScript代码,该函数接受两个参数分别为数组、索引值,要求在不改变原数组的情况下返回删除了索引项的新数组。
使用filter
const _delete = (array,index) => {
// 补全代码
let arr = array.filter((item, index1) => {
return index1 != index
})
return arr
}
2
3
4
5
6
7
使用splice
const _delete2 = (array,index) => {
// 补全代码
let arr = [...array]
arr.splice(index, 1)
return arr
}
2
3
4
5
6
# js10计数器
- 初次调用返回值为1
- 每个计数器所统计的数字是独立的
const closure = () => {
// 补全代码
let count = 0;
return function() {
count ++
return count
}
}
let res = closure()
console.log(res())
console.log(res())
console.log(res())
2
3
4
5
6
7
8
9
10
11
12
# js11列表动态渲染
请补全JavaScript代码,将预设代码中的"people"数组渲染在页面中。实现下面的列表:
- 牛油1号 20岁
- 牛油2号 21岁
- 牛油3号 19岁
<ul></ul>
<script>
var people = [
{ name: '牛油1号', id: 1, age: 20 },
{ name: '牛油2号', id: 2, age: 21 },
{ name: '牛油3号', id: 3, age: 19 },
]
var ul = document.querySelector('ul');
// 补全代码
people.forEach(item => {
var li = document.createElement('li')
li.innerText = `${item.name} ${item.age}`
ul.appendChild(li)
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# js12模版字符串
请补全JavaScript代码,实现以下功能:
- 根据已有的person对象的注册时间求出距离当前时间的天数(天数向下取整)。
- 将获得的天数和person数据拼接成字符串,作为h2标签的内容。 注意:使用模板字符串进行字符串拼接,字符串最终内容如:尊贵的牛客网2级用户小丽您好,您已经注册牛客网3天啦~
var person = {
level: '2',
name: '小丽',
registTime: '2021-11-01',
}
var h2 = document.querySelector('h2');
// 补全代码
function getDifferDay(date) {
var date_1 = Date.now()
var date_2 = Date.parse(date)
var diffDate = date_1 - date_2
var totalDays = Math.floor(diffDate / (1000 * 3600 * 24))
return totalDays
}
h2.innerHTML = `尊贵的牛客网${person.level}级用户${person.name}您好,您已经注册牛客网${getDifferDay(person.registTime)}天啦~`
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# js13类继承
请补全JavaScript代码,完成类的继承。要求如下:
- "Chinese"类继承于"Human"类
- "Human"类实现一个函数"getName",返回该实例的"name"属性
- "Chinese"类构造函数有两个参数,分别为"name"、"age"
- "Chinese"类实现一个函数"getAge",返回该实例的"age"属性
class Human {
constructor(name) {
this.name = name
this.kingdom = 'animal'
this.color = ['yellow', 'white', 'brown', 'black']
}
// 补全代码
getName() {
return this.name
}
}
// 补全代码
class Chinese extends Human {
constructor(name, age) {
super(name)
this.age = age
}
getAge() {
return this.age
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# js14参数解析器
请补全JavaScript代码,要求将字符串参数URL中的参数解析并以对象的形式返回。
输入:
getParams('https://nowcoder.com/online?id=1&salas=1000')
输出:
{id:1, salas: 100}
const _getParams = (url) => {
// 补全代码
let param = url.split("?")[1]
let obj = {}
let params = param.split('&')
params.forEach(item => {
let temp = item.split('=')
obj[temp[0]] = temp[1]
})
return obj
}
2
3
4
5
6
7
8
9
10
11
// 利用正则表达式
// 利用正则表达式
const _getParams2 = (url) => {
let arr = url.match(/(\w+)=(\w+)/gi)
let obj = {}
arr.map(item => {
let [key, value] = item.split('=')
obj[key] = value
})
return obj
}
2
3
4
5
6
7
8
9
10
# js15生成页码
请补全JavaScript代码,要求根据参数动态生成"li"标签页码并插入"ul"标签下。要求如下:
- "allItem"为总数据项个数,"pageItem"为每页的数据项个数
- "li"标签内容为当前页码数,页码从1开始
输入:
_createPage(13,2)
输出:
"li"长度为7,"li"内容依次为"1","2","3","4","5","6","7"
const _createPage = (allItem, pageItem) => {
// 补全代码
// let count = allItem % pageItem == 0 ? allItem / pageItem : allItem / pageItem + 1
let count = Math.ceil(allItem / pageItem) // 向上取整
let ul = document.getElementById('ul')
for (let i = 1; i <= count; i++) {
let li = document.createElement('li')
li.innerHTML = i
ul.appendChild(li)
}
}
_createPage(13, 2)
2
3
4
5
6
7
8
9
10
11
12
# js16总成绩排名
请补全JavaScript代码,要求将数组参数中的对象以总成绩(包括属性"chinese"、"math"、"english")从高到低进行排序并返回。
const _rank = array => {
// 补全代码
return array.sort((a, b) => {
return (b.chinese + b.math + b.english) - (a.chinese + a.math + a.english)
})
}
let grade = [{name: 'nowcoder1',chinese: 73,math: 80,english: 72},{name: 'nowcoder2',chinese: 59,math: 53,english: 36},{name: 'nowcoder3',chinese: 94,math: 96,english: 94}]
console.log(_rank(grade))
2
3
4
5
6
7
8
# js17子字符串频次
请补全JavaScript代码,该函数接受两个参数分别为字符串、子字符串,要求返回子字符串在字符串中出现的频次。
const _searchStrIndexOf = (str, target) => {
// 补全代码
let n = target.length
let count = 0
for(let i = 0; i < str.length - n + 1; i++) {
if ((str.slice(i,i+n)) === target) {
count++
}
}
return count
}
2
3
4
5
6
7
8
9
10
11
使用split分隔符
// 使用split分隔
const _searchStrIndexOf2 = (str, target) => {
return str.split(target).length - 1
}
2
3
4
正则表达式
const _searchStrIndexOf3 = (str, target) => {
let reg = new RegExp(target, 'g')
return str.match(reg).length
}
2
3
4
indexOf
// 使用indexOf
const _searchStrIndexOf4 = (str, target) => {
let index = str.indexOf(target)
let count = 0
while(index > -1) {
index = str.indexOf(target, index + 1)
count++
}
return count
}
2
3
4
5
6
7
8
9
10
# 字符串截取方法
slice()
方法可通过指定的开始和结束位置,提取字符串的某个部分,并以新的字符串返回被提取的部分。string.slice(start, end);
1start(必需):规定从何处开始选取。如果是负数,那么它规定从字符串尾部开始算起的位置。也就是说,-1 指最后一个字符,-2 指倒数第二个字符,以此类推。
end(可选):规定从何处结束选取,即结束处的字符下标。如果没有指定该参数,那么截取的字符串包含从 start 到结束的所有字符。如果这个参数是负数,那么它规定的是从数组尾部开始算起的字符。
substring
方法用于提取字符串中介于两个指定下标之间的字符。该方法返回一个新的字符串,该字符串值包含 stringObject 的一个子字符串,其内容是从 start 处到 stop-1 处的所有字符,其长度为 stop 减 start。string.substring(start, stop)
1start(必需):一个非负的整数,规定要提取的子串的第一个字符在 stringObject 中的位置。
stop(可选):一个非负的整数,比要提取的子串的最后一个字符在 stringObject 中的位置多 1。
如果 start 与 end 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。 如果 start 比 end 大,那么该方法在提取子串之前会先交换这两个参数。 如果 start 或 end 为负数,那么它将被替换为 0。
substr
方法用于返回一个从指定位置开始的指定长度的子字符串。stringObject.substr(start, length);
1start(必需):所需的子字符串的起始位置。字符串中的第一个字符的索引为 0。 length(可选):在返回的子字符串中应包括的字符个数。
indexOf()
返回字符串中匹配子串的第一个字符的下标。lastIndexOf()
该方法返回从右向左出现某个字符或字符串的首个字符索引值
# js18继承
请补全JavaScript代码,实现以下功能:
- 给"Human"构造函数的原型对象添加"getName"方法,返回当前实例"name"属性
- 将"Chinese"构造函数继承于"Human"构造函数
- 给"Chinese"构造函数的原型对象添加"getAge"方法,返回当前实例"age"属性
function Human(name) {
this.name = name
this.kingdom = 'animal'
this.color = ['yellow', 'white', 'brown', 'black']
}
function Chinese(name,age) {
Human.call(this,name)
this.age = age
this.color = 'yellow'
}
// 补全代码
Human.prototype.getName = function() {
return this.name
}
Chinese.prototype = new Human()
Chinese.prototype.getAge = function() {
return this.age
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# js19判断斐波那契数组
请补全JavaScript代码,要求以Boolean的形式返回参数数组是否为斐波那契数列。在数学上,斐波那契数列以如下方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N) 注意:
- [0,1,1]为最短有效斐波那契数列
<script type="text/javascript">
const _isFibonacci = array => {
// 补全代码
let n = array.length
if (n == 0 || (n == 1 && array[0] != 0)) {
return false
}
if (n == 2 && array[0] == 0 && array[1] == 1) {
return false
}
for (let i = 2; i < n; i++) {
if (array[i] != array[i-1] + array[i-2]) {
return false
}
}
return true
}
arr = [0,1,1,2,3,5]
console.log(_isFibonacci(arr))
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# js20数组扁平化
请补全JavaScript代码,要求将数组参数中的多维数组扩展为一维数组并返回该数组。 注意:
- 数组参数中仅包含数组类型和数字类型
输入:
[1,[2,[3,[4]]]]
输出:
[1,2,3,4]
2
3
4
// 使用toString() + split()
const _flatten = arr => {
// 补全代码
return arr.toString().split(',').map(item => Number(item))
}
2
3
4
5
// reduce 实现
const _flatten2 = arr => {
// 补全代码
return arr.reduce((target, item) => {
return target.concat(Array.isArray(item) ? _flatten2(item) : item)
}, [])
}
2
3
4
5
6
7
// join() + split() 实现
const _flatten3 = arr => {
// 补全代码
return arr.join(',').split(',').map(item => Number(item))
}
2
3
4
5
// 递归
const _flatten4 = arr => {
// 补全代码
let res = []
arr.forEach(item => {
if (Array.isArray(item)) {
res = res.concat(_flatten3(item))
}else {
res.push(item)
}
})
return res
}
2
3
4
5
6
7
8
9
10
11
12
13
# js21数组过滤
请补全JavaScript代码,要求根据下拉框选中的条件变换重新渲染列表中展示的商品,且只展示符合条件的商品。 注意:
- 必须使用DOM0级标准事件(onchange)
- 建议使用ES6的filter方法
var cups = [
{ type: 1, price: 100, color: 'black', sales: 60, name: '牛客logo马克杯' },
{ type: 2, price: 40, color: 'blue', sales: 100, name: '无盖星空杯' },
{ type: 4, price: 60, color: 'green', sales: 200, name: '老式茶杯' },
{ type: 3, price: 50, color: 'green', sales: 600, name: '欧式印花杯' }
]
var select = document.querySelector('select');
var ul = document.querySelector('ul');
// 补全代码
show(cups)
// if else
// select.onchange = function(e) {
// var index = select.value
// var arr = []
// if (index == 0) {
// arr = cups
// }else if(index == 1) {
// arr = cups.filter(item => item.sales < 100)
// }else if (index == 2) {
// arr = cups.filter(item => item.sales >= 100 && item.sales <= 500)
// }else if(index == 3) {
// arr = cups.filter(item => item.sales > 500)
// }
// show(arr)
// }
select.onchange = function() {
switch(this.value) {
case "1":
show(cups.filter(item => item.sales < 100))
break;
case "2":
show(cups.filter(item => item.sales >= 100 && item.sales <= 500))
break;
case "3":
show(cups.filter(item => item.sales > 500))
break;
}
}
function show(arr) {
var str = ``
arr.forEach(item => {
str += `<li>${item.name}</li>`
})
ul.innerHTML = str
}
// function test() {
// var select = document.querySelector('select');
// var ul = document.querySelector('ul');
// select.value = 1;
// select.onchange();
// var lis = document.querySelectorAll('li');
// var first = lis.length == 1 && lis[0].innerText == '牛客logo马克杯';
// var result = first; return result;
// }
// console.log(test())
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
49
50
51
52
53
54
55
56
# js22判断质数
请补全JavaScript代码,要求在Number对象的原型对象上添加"_isPrime"函数,该函数判断调用的对象是否为一个质数,是则返回true,否则返回false。
// 这里参数得是number 或者里面的number用this代替
Number.prototype._isPrime = function(number) {
if (number <= 1) {
return false
}
for(let i = 2; i <= number-1; i++) {
if (number % i == 0) {
return false
}
}
return true
}
2
3
4
5
6
7
8
9
10
11
12
# js23验证是否是身份证
请补全JavaScript代码,要求以Boolean的形式返回字符串参数是否符合身份证标准。 注意:
- 无需考虑地区信息、出生日期、顺序码与校验码的验证
输入:
_isCard('21062319980907888X')
输出:
true
<script>
const _isCard = number => {
// 补全代码
let reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
return reg.test(number)
}
console.log(_isCard('21062319980907888X'))
</script>
2
3
4
5
6
7
8
9
search(reg)
方法
方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置。
replace(searchvalue,newvalue)
方法
参数:searchvalue:必需。规定子字符串或要替换的模式的 RegExp 对象。
newvalue: 必需。一个字符串值。规定了替换文本或生成替换文本的函数。
方法用于在字符串中用一些字符串替换另一些字符串,或替换一个与正则表达式匹配的子串,返回值为一个新字符串。
match(regexp)
方法
参数:regexp:必需。规定要匹配的模式的 RegExp 对象。
match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
这个方法的行为在很大程度上有赖于 regexp 是否具有标志 g。如果 regexp 没有标志 g,那么 match() 方法就只能执行一次匹配。如果没有找到任何匹配的文本将返回 null。否则,它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。
"hello abcia ajlfka".match(/\b[a-z]+/g)
// 结果为:Array(3) [ "hello", "abcia", "ajlfka" ]
2
split(separator*,*limit)
方法
参数:separator:可选。正则表达式或字符串。
limit:可选。指定返回的数组的最大长度。
test(要检测的字符串)
方法
最常用的方法,用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。
const reg = /[0-9]-[a-z]/;
reg.test("1-a"); // true
2
exec(需要匹配的字符串)
方法
exec() 方法用于检索字符串中的正则表达式的匹配。如果字符串中有匹配的值返回该匹配值,否则返回 null。
var str="086-15911112233";
var patt=/^[0-9]{3}-[\w]*$/g;
var result=patt.exec(str); // result结果为086-15911112233
2
3
# js24Symbol
请补全JavaScript代码,要求以键/值对的对象形式返回参数数组。要求如下:
- 键名的数据类型为Symbol
- 键值为当前数组项
- Symbol的描述为当前数组项
- 返回普通对象
const _symbolKey = array => {
// 补全代码
let obj = {}
array.forEach(item => {
obj[Symbol(item)] = item
})
return obj
}
2
3
4
5
6
7
8
# js25相同的Set
请补全JavaScript代码,要求以boolean的形式返回两个Set对象参数是否一样,是则返回true,否则返回false。
const _isSameSet = (s1, s2) => {
// 补全代码
if (s1.size != s2.size) {
return true
}else {
for(let key of s1.keys()) {
if (!s2.has(key)) {
return false
}
}
}
return true
}
2
3
4
5
6
7
8
9
10
11
12
13
// 使用扩展符和every
const _isSameSet = (s1, s2) => {
// 补全代码
if (s1.size != s2.size) {
return true
}
return [...s1].every(item => s2.has(item))
}
2
3
4
5
6
7
8
# Set迭代
for(let val of s.values()) {
console.log(val)
}
for(let val of s.keys()) {
console.log(val)
}
for(let val of s[Symbol.iterator]()) {
console.log(val)
}
2
3
4
5
6
7
8
9
values()是默认迭代器,所以可以直接对集合实例使用扩展操作,把集合转换为数组
const s = new Set(['val1', 'val2'])
for(let temp of s.entries()) {
console.log(temp)
}
// ['val1', 'val1']
// ['val2', 'val2']
2
3
4
5
6
# js26Getter
请补全JavaScript代码,完成名为"Rectangle"的矩形类。要求如下:
- 构造函数只包含两个参数,依次为"height"、"width"
- 设置Getter,当获取该对象的"area"属性时,返回该对象"height"与"width"属性的乘积
class Rectangle {
// 补全代码
constructor(width, height) {
this.width = width
this.height = height
}
get area() {
return this.width * this.height
}
}
console.log(new Rectangle(12,12).area)
2
3
4
5
6
7
8
9
10
11
12
# js27控制动画
请补全代码,要求当滑动id为"range"的滑块控件时可以改变id为"rect"的矩形旋转速度。要求如下:
id为"rect"的矩形初始动画周期为10秒
id为"range"的滑块控件默认值为1、最小值为、最大值为10、滑动间隔为1
当滑动滑块值为1时,矩形动画周期为10秒、当...,为...、当滑动滑块值为10时,矩形动画周期为1秒 注意:
必须使用DOM0级标准事件(onchange)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#rect {
width: 120px;
height: 100px;
background-color: black;
/*补全代码*/
animation: rect 10s linear infinite;
}
@keyframes rect {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<!-- 补全代码 -->
<div id="rect"></div>
<input id="range" type="range" step="1" defaultValue="1" value="1" min="1" max="10" />
<script type="text/javascript">
// 补全代码
let div = document.getElementById("rect")
let input = document.getElementById("range")
let time = 10
input.onchange = function() {
div.style.animationDuration = 11 - this.value + 's'
}
</script>
</body>
</html>
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
# css动画
# CSS Transition
Transition主要用于元素属性变化时提供时间上的过渡效果。Transition总共有四条属性:
属性 | 说明 |
---|---|
transition-property | 过渡效果应用的属性,比如元素的width或height,默认为all 即如果不指定则会将效果施加到所有支持的属性上 |
transition-duration | 过渡效果持续时间,必须设置此项,默认为0s |
transition-timing-function | 过渡效果变化速度,默认为ease,慢快慢 |
transition-delay | 过渡效果延迟多少时间后开始执行,默认为0s |
复合使用
transition: <property> || <duration> || <timing-function> || <delay>
transition: width 3s liner 1s
transition: width 3s, height 1s 1s
2
3
# CSS Animation
Animation相比Transition可以实现更加复杂的动画效果,而且不需要触发即可播放。更强的功能带来了更多的属性:
属性 | 说明 |
---|---|
animation-name | 关键帧(@keyframes)名称 |
animation-duration | 动画持续时间,必须设置此项,默认为0s |
animation-timing-function | 动画播放速度,默认为ease,慢快慢 |
animation-delay | 动画延迟多少时间后开始执行,默认为0s |
animation-iteration-count | 动画循环播放次数,默认为1,取infinite可以无限循环播放 |
animation-direction | 动画播放方向,可选值如下: normal 默认值,每个动画循环结束重置到起点重新开始; alternate 动画正反向交替播放; reverse 反向播放动画,每周期结束动画由尾到头播放; alternate-reverse 第一次反向播放,然后正反向交替播放; |
animation-fill-mode | 动画不播放时(播放完成时 或 有延迟未开始播放时)的状态,可选值如下: none 动画未播放时不应用任何动画样式; forwards 动画播放完成后元素保持最后一帧状态; backwards 动画未播放时元素应用动画第一帧状态; both 同时应用forwards和backwards;<br/ |
animation-play-state | 控制动画播放和暂停,可选值如下: running 运行; 暂停 paused; |
Animation需要用到@keyframes,这个用来描写关键帧信息,语法如下:
@keyframes animationname {keyframes-selector {css-styles;}}
keyframes-selector表示动画播放过程的时刻,可以用0%、50%、100%等表示,也可以用from表示0%、用to表示100%。
timing-function
Transition和Animation里都有 timing-function
这个属性。这个属性控制的是动画的播放速度,自带的可选值有以下几个:
取值 | 说明 |
---|---|
linear | 以相同速度开始至结束的过渡效果(等于 cubic-bezier(0,0,1,1)) |
ease | 慢速开始,然后变快,然后慢速结束的过渡效果(cubic-bezier(0.25,0.1,0.25,1)) |
ease-in | 以慢速开始的过渡效果(等于 cubic-bezier(0.42,0,1,1)) |
ease-out | 以慢速结束的过渡效果(等于 cubic-bezier(0,0,0.58,1)) |
ease-in-out | 以慢速开始和结束的过渡效果(等于 cubic-bezier(0.42,0,0.58,1)) |
cubic-bezier(n,n,n,n) | 在 cubic-bezier 函数中定义自己的值 |
steps(n,start/end) | 将每一段关键帧分成n段,每次播放这一段中前面或后面一格画面 start 显示尾部画面、end 显示头部画面 |
step-start | 等于steps(1,start) |
step-end | 等于steps(1,end) |
# CSS Transform
Transform可以实现对元素的位移、缩放、旋转、扭曲等功能(有点类似于PS中的自由变换,但比自由变换要强大很多)。利用Transform可以实现很多丰富的视觉效果,CSS动画很多时候都需要结合Transform使用才能得到优异的效果。Transform主要可设置效果如下:
属性 | 说明 |
---|---|
none | 不进行转换 |
matrix(n,n,n,n,n,n) | 2D变换 |
matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n) | 3D变换 |
translate(x,y) | 2D平移 |
translate3d(x,y,z) | 3D平移 |
translateX(x) | 沿X轴平移 |
translateY(y) | 沿Y轴平移 |
translateZ(z) | 沿Z轴平移 |
scale(x[,y]?) | 2D缩放 |
scale3d(x,y,z) | 3D缩放 |
scaleX(x) | 沿X轴缩放 |
scaleY(y) | 沿Y轴缩放 |
scaleZ(z) | 沿Z轴缩放 |
rotate(angle) | 2D旋转 |
rotate3d(x,y,z,angle) | 3D旋转 |
rotateX(angle) | 沿X轴旋转 |
rotateY(angle) | 沿Y轴旋转 |
rotateZ(angle) | 沿Z轴旋转 |
skew(x-angle,y-angle) | 沿X/Y轴倾斜转换 |
skewX(angle) | 沿X轴倾斜转换 |
skewY(angle) | 沿Y轴倾斜转换 |
perspective(n) | 为3D转换元素定义透视视图 |
# js28Map保存节点
请补全JavaScript代码,要求将页面中的"p"标签以键名的形式保存在Map对象中,键名所对应的键值为该"p"标签的文字内容。
const _elementKey = () => {
// 补全代码
const map = new Map()
let ps = document.getElementsByTagName('p')
for(let i = 0; i < ps.length; i++) {
map.set(ps[i], ps[i].innerHTML)
}
return map
}
_elementKey()
2
3
4
5
6
7
8
9
10
# 获取Dom元素的方法
根据ID获取元素
document.getElementById('id名称')
1根据标签获取元素
// 获取到的是HTMLCollection 集合,也就是 伪数组 document.getElementsByTagName('标签名称')
1
2根据name属性获取元素
document.getElementsByName('name名称')
1根据class属性获取元素
// 返回的也是伪数组,存在兼容性问题 document.getElementsByClassName('类名')
1
2根据选择器获取元素
// 返回文档中匹配指定的选择器组的第一个元素 document.querySelector() // 返回文档中匹配指定的选择器组的所有元素 document.querySelectorAll()
1
2
3
4
# js29全选
请补全JavaScript代码,实现以下效果:
选中"全选"框,以下所有选项全部勾选。
把"全选"框从选中状态勾选成未选中状态,其他复选框全部取消选中效果。
当其他复选框全部选中,"全选框"为选中状态。
当其他复选框有一个未选中,"全选框"取消选中状态。 注意:
必须使用DOM0级标准事件(onchange)
var all = document.querySelector('#all')
var options = Array.from(document.querySelectorAll('.item'))
// 实现全选和取消全选
all.onchange = function() {
var checked = this.checked
options.forEach(item => {
item.checked = checked
})
}
// 实现取消某一项来取消全选
options.forEach(item => {
item.onchange = function() {
// 标记是否全选
var isAllCheck = true
options.forEach(item => {
if (!item.checked) {
isAllCheck = false
return
}
})
all.checked = isAllCheck
}
})
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
# js30回文字符串
请补全JavaScript代码,要求以boolean的形式返回参数字符串是否为回文字符串。
// 方法一:字符串反转
const _isPalindrome = string => {
// 补全代码
let arr = string.split("")
return arr.join('') === arr.reverse().join('')
}
2
3
4
5
6
const _isPalindrome2 = string => {
let length = string.length
for(let i = 0; i < length / 2; i++) {
if (string.charAt(i) != string.charAt(length - i - 1)) {
return false
}
}
return true
}
2
3
4
5
6
7
8
9
# String属性和方法
属性
length
字符串(String)使用长度属性length来计算字符串的长度
prototype
用来给对象添加属性或方法,并且添加的方法或属性在所有的实例上共享。因此也常用来扩展js内置对象
constructor
返回创建该对象的数组函数
方法
charAt()
语法:stringObject.charAt(index)
返回指定位置的字符 (不在范围返回空字符串)
charCodeAt()
语法:string.includes(searchvalue, start) 判断字符串是否包含指定的子字符串 返回在指定的位置的字符的 Unicode 编码
concat()
语法:stringObject.concat(stringX,stringX,…,stringX)
用于连接两个或多个字符串,并且返回一个新的字符串,原来的字符串不受影响
endsWith()
语法:string.endsWith(searchvalue, length)
判断字符串是否以指定的子字符串结尾(区分大小写)
fromCharCode()
语法:String.fromCharCode(numX,numX,…,numX)
fromCharCode() 可接受一个指定的 Unicode 值,然后返回一个字符串
indexOf()
语法:stringObject.indexOf(searchvalue,fromindex)
返回某个指定的字符串值在字符串中首次出现的位置,第二个参数是开始检索的位置。 如果查找不到返回-1
includes()
语法:string.includes(searchvalue, start)
判断字符串是否包含指定的子字符串
lastIndexOf()
语法:stringObject.lastIndexOf(searchvalue,fromindex) 返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索
match()
语法:stringObject.match(searchvalue) stringObject.match(regexp) 查找字符串中特定的字符或找到一个或多个正则表达式的匹配,并且如果找到的话,则返回这个字符或存放匹配结果的数组。
replace()
语法:stringObject.replace(regexp/substr,replacement) 在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。返回一个新的字符串,是用 replacement 替换了 regexp 的第一次匹配或所有匹配之后得到的repeat()
语法:string.repeat(count) 字符串复制指定次数
replaceAll()
语法:const newStr = str.replaceAll(regexp|substr, newSubstr|function) 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串,该函数会替换所有匹配到的子字符串
search()
语法:stringObject.search(regexp) 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,返回stringObject 中第一个与 regexp 相匹配的子串的起始位置
slice()
语法:stringObject.slice(start,end) 提取字符串的某个部分,并以新的字符串返回被提取的部分
split()
语法:string.split(separator,limit) 用于把一个字符串分割成字符串数组
substr()
语法:stringObject.substr(start,length) 在字符串中抽取从 start 下标开始的指定数目的字符
与 slice() 和 substr() 方法不同的是,substring() 不接受负的参数。
toLowerCase()/toUpperCase()
把字符串转换为小写/把字符串转换为大写
trim()
用于删除字符串的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等
valueOf()
语法:stringObject.valueOf() 返回 String 对象的原始值
toString()
返回一个表示 String 对象的值
# js31Proxy计数器
请补全JavaScript代码,请给参数对象添加拦截代理功能,并返回这个代理,要求每当通过代理调用该对象拥有的属性时,"count"值加1,否则减1。
let count = 0
const _proxy = object => {
// 补全代码
return new Proxy(object,{
get(target, prop, receiver) {
Reflect.get(target, prop, receiver)
if (prop in target) {
count ++
} else {
count --
}
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Proxy
const proxy = new Proxy(target, handle);
- target,被代理的对象
- handle,拦截规则
get操作
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]。
get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象,也就是所谓的接收器),其中最后一个参数可选。
const proxy = new Proxy(obj, {
// target 目标对象;propKey 属性名;receiver 实例本身;
get: function(target, propKey, receiver) {
return 10;
}
})
2
3
4
5
6
set操作
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值、Proxy 实例本身 其中最后一个参数可选。
const proxy = new Proxy(obj, {
// target 目标对象;propKey 属性名;value 属性值 receiver 实例本身;
get: function(target, propKey, value, receiver) {
target[propKey] = value
}
})
2
3
4
5
6
# js32Proxy拦截器
请补全JavaScript代码,请给参数对象添加拦截代理功能并返回这个代理。要求如下:
- 该函数接收多个参数,首个参数为对象,从第二个参数(包括)往后皆是该对象的属性名
- 通过该函数给首个参数对象添加拦截器功能,每当该对象访问到该函数第二个参数(包括)往后的属性时,返回"noright"字符串,表示无权限。
const _proxy = (object,...prototypes) => {
// 补全代码
return new Proxy(object, {
get(target, prop, receiver) {
if (prototypes.indexOf(prop) != -1) {
return 'noright'
} else {
return target[prop]
}
}
})
}
2
3
4
5
6
7
8
9
10
11
12
# Reflect
在JavaScript中,Reflect是一个内置对象,它提供了一组用于拦截JavaScript操作的方法。这些方法与Proxy的拦截器方法一一对应,并且提供了一些便捷的功能。
Reflect.get(target, propertyKey[, receiver])
这个方法用于获取一个对象上的属性值。它与直接访问属性的方式类似,但是它会返回一个值,而不是抛出异常。
target
:要从中获取属性的对象。propertyKey
:要获取的属性的名称。receiver
:可选参数。如果target
对象是一个代理对象,那么receiver
就是代理对象所在的上下文。
Reflect.set(target, propertyKey, value[, receiver])
这个方法用于设置一个对象上的属性值。与直接设置属性的方式类似,但是它会返回一个布尔值,表示属性是否设置成功。
target
: 要设置的对象propertyKey
: 要设置的属性的名称value
: 要设置的属性的值receiver
:可选参数。如果target
对象是一个代理对象,那么receiver
就是代理对象所在的上下文。
Reflect.has(target, propertyKey)
这个方法用于判断一个对象是否包含某个属性。与
in
运算符类似,但是它会返回一个布尔值,表示属性是否存在。target
:要检查属性是否存在的对象。propertyKey
:要检查的属性的名称
Reflect.deleteProperty(target, propertyKey)
这个方法用于删除一个对象上的属性。与
delete
运算符类似,但是它会返回一个布尔值,表示属性是否删除成功。target
:要删除属性的对象。propertyKey
:要删除的属性的名称。
Reflect.construct(target, argumentsList[, newTarget])
这个方法用于创建一个实例对象。与
new
运算符类似,但是它可以在不使用new
运算符的情况下调用构造函数。target
:要创建实例的构造函数。argumentsList
:一个类数组对象,包含构造函数的参数。newTarget
:可选参数。如果指定了newTarget
,那么它将被用作this
关键字的值。
Reflect.apply(target, thisArgument, argumentsList)
这个方法用于调用一个函数。与直接调用函数的方式类似,但是它可以在不使用圆括号调用函数的情况下调用函数,并且可以指定函数的
this
值和参数列表。target
:要调用的函数。thisArgument
:可选参数。用作函数的this
值。argumentsList
:一个类数组对象,包含函数的参数。
# js33监听对象
请补全JavaScript代码,要求如下:
- 监听对象属性的变化
- 当"person"对象的属性发生变化时,页面中与该属性相关的数据同步更新
注意:
- 必须使用Object.defineProperty实现且触发set方法时更新视图
- 可以使用预设代码"_render"函数
var ul = document.querySelector('ul');
var person = { sex: '男', age: '25', name: '王大锤', height: 28, weight: 32 };
const _render = element => {
var str = `<li>姓名:<span>${person.name}</span></li>
<li>性别:<span>${person.sex}</span></li>
<li>年龄:<span>${person.age}</span></li>
<li>身高:<span>${person.height}</span></li>
<li>体重:<span>${person.weight}</span></li>`
element.innerHTML = str;
}
_render(ul);
// 补全代码
function defineReactive(target, key, value) {
Object.defineProperty(target, key, {
get: function() {
return value
},
set: function(newValue) {
if (newValue != value) {
value = newValue
_render(ul)
}
}
})
}
function Observer(target) {
if (typeof target !== 'object' || target == null) {
return target
}
for(let key in target) {
defineReactive(target, key, target[key])
}
}
Observer(person)
person.name = '张三'
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
# Object.defineproperty
Object.defineproperty 的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineproperty(obj, prop, desc)
obj : 第一个参数就是要在哪个对象身上添加或者修改属性
prop : 第二个参数就是添加或修改的属性名
desc : 配置项,一般是一个对象
第三个参数里面的配置
writable: 是否可重写
value: 当前值
get: 读取时内部调用的函数
set: 写入时内部调用的函数
enumerable: 是否可以遍历
configurable: 是否可再次修改配置项
# js34购物面板
请补全JavaScript代码,要求如下:
- 当点击"-"按钮时,商品数量减1
- 当点击"+"按钮时,商品数量加1
- 每当点击任意按钮时,购物面板中相关信息必须同步更新
注意:
- 必须使用DOM0级标准事件(onclick)
// 计算总价也可以采用两个价格乘以单价之和的方式
let total_span = document.querySelector("#total")
let total_value = Number(total_span.innerText)
function setClickFun(sub, span, add, price) {
let span_dom = document.querySelector(span)
let sub_btn = document.querySelector(sub)
let add_btn = document.querySelector(add)
sub_btn.onclick = function() {
total_value = Number(total_span.innerText)
let value = Number(span_dom.innerText) - 1
if (value < 0) {return}
span_dom.innerText = value
total_span.innerText = total_value - price
}
add_btn.onclick = function() {
total_value = Number(total_span.innerText)
let value = Number(span_dom.innerText) + 1
span_dom.innerText = value
total_span.innerText = total_value + price
}
}
setClickFun('#zjtaiduola', '#zjsl', '#zjtaishaola', 28)
setClickFun('#kltaiduola', '#klsl', '#kltaishaola', 5)
document.querySelector('#zjtaishaola').click();
document.querySelector('#kltaishaola').click();
console.log(document.querySelector('#total').innerText === '33')
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
# js35接口
请补全JavaScript代码,完成函数的接口功能。要求如下:
- 函数接收两种类型的参数,分别为"get?"和"update?name=xxx&to=yyy","name"、"to"为参数,"xxx"、"yyy"分别为参数对应的值。
- 当参数为"get?"时,返回data数据
- 当参数为"update?name=xxx&to=yyy"时,将data中所有"=name"为"xxx"的项,更改为"name"值为"yyy"
let data = [
{name: 'nowcoder1'},
{name: 'nowcoder2'}
]
const _api = string => {
// 补全代码
let arr = string.split('?')
if (arr[0] == 'get') {
return data
} else if (arr[0] == 'update') {
let param_arr = arr[1].split('&')
let name = param_arr[1].split('=')[1]
data.forEach(item => {
item.name = name
})
} else {
return null
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# js36切换Tab栏目
请补全JavaScript代码,实现效果如下:
- 当点击某个栏目(题库、面试、学习、求职)时,该栏目背景色变为'#25bb9b',其它栏目背景色位'#fff'。
- 当选中某个栏目时,下方内容就展示索引值相同的类名为".items"的"li"元素
注意:
- 必须使用DOM0级标准事件(onclick)
- 已使用自定义属性存储了栏目的索引值。点击栏目获取索引值,使用索引值控制类名为"items"下的"li"元素
<style>
ul {
padding: 0;
margin: 0;
list-style: none;
}
.options li {
float: left;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
border: solid 1px #ddd;
}
.items li {
width: 405px;
height: 405px;
display: none;
border: solid 1px #ddd;
}
</style>
<ul class='options'>
<li data-type="0" style='background-color: #25bb9b;'>题库</li>
<li data-type="1">面试</li>
<li data-type="2">学习</li>
<li data-type="3">求职</li>
</ul>
<ul class='items'>
<li style="display: block;">牛客题库,包含编程题、选择题等</li>
<li>为你的面试提供一站式服务</li>
<li>校招学习来牛客</li>
<li>求职中有什么难题,可以联系我们</li>
</ul>
<script>
var options = document.querySelector('.options');
var optionItems = [].slice.call(document.querySelectorAll('.options li'));
var items = [].slice.call(document.querySelectorAll('.items li'));
// 补全代码
// 编译不能通过
// for(let i = 0; i < optionItems.length; i++) {
// optionItems[i].onclick = function() {
// optionItems[i].style.backgroundColor = '#25bb9b'
// items[i].style.display = 'block'
// for (let j = 0; j < optionItems.length; j++) {
// console.log(i)
// if (i != j) {
// optionItems[j].style.backgroundColor = '#ffffff'
// items[j].style.display = 'none'
// }
// }
// }
// }
options.onclick = function(e) {
optionItems.forEach((item, index) => {
if (index == e.target.getAttribute('data-type')) {
item.style.backgroundColor = '#25bb9b'
items[index].style.display = 'block'
} else {
item.style.backgroundColor = '#ffffff'
items[index].style.display = 'none'
}
})
}
var event ={target: optionItems[2]};
options.onclick(event);
var result = optionItems[2].style.backgroundColor == 'rgb(37, 187, 155)';
console.log(result)
</script>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 事件委托
事件委托就是把原本需要绑定在子元素上的事件(onclick、onkeydown 等)委托给它的父元素,让父元素来监听子元素的冒泡事件,并在子元素发生事件冒泡时找到这个子元素。
# 为什么要使用事件委托
在 JavaScript 中,页面内事件处理程序的个数会直接影响页面的整体性能,因为每个事件处理程序都是对象,对象会占用内存,内存中的对象越多,页面的性能则越差。此外,事件处理程序需要与 DOM 节点进行交互,访问 DOM 的次数越多,引起浏览器重绘和重排的次数也就越多,从而影响页面的性能。
重绘是指当元素样式改变时,浏览器会根据元素的新样式重新绘制元素的外观。重排是指当 DOM 树的一部分发生变化时(例如元素尺寸改变),浏览器会重新创建 DOM 树。
当页面中很多表格或列表需要添加事件时,如果逐个添加那就太麻烦了,但是使用事件委托就能极大的减轻我们的工作量,同时也能提高页面的性能。
# 事件委托实现原理
- 确定要添加事件元素的父级元素;
- 给父元素定义事件,监听子元素的冒泡事件;
- 使用 event.target 来定位触发事件冒泡的子元素。
使用事件委托时,并不是说把事件委托给随意一个父元素就行。因为事件冒泡的过程也需要消耗时间,距离越远,所需的时间也就越长,所有最好在直接父元素上使用事件委托。
<ul id='list'>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
var ul = document.querySelector('#list')
ul.onclick = function(e) {
console.log(e.target.innerHTML)
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
# 事件委托的优点
- 减小内存消耗
使用事件委托可以大量节省内存,减少事件的定义,通过上面的示例可以看出,要为 ul 标签下的所有 li 标签添加点击事件,如果分别为每个 li 标签绑定事件,不仅写起来比较繁琐,而且对内存的消耗也非常大。而使用事件委托的方式将点击事件绑定到 ul 标签上,就可以实现监听所有 li 标签,简洁、高效。
动态绑定事件
在网页中,有时我们需要动态增加或移除页面中的元素,比如上面示例中动态的在 ul 标签中添加 li 标签,如果不使用事件委托,则需要手动为新增的元素绑定事件,同时为删除的元素解绑事件。而使用事件委托就没有这么麻烦了,无论是增加还是减少 ul 标签中的 li 标签,即不需要再为新增的元素绑定事件,也不需要为删除的元素解绑事件。
所以使用事件委托动态绑定事件可以减少很多重复工作的。
要使用事件委托,需要保证事件能够发生冒泡,适合使用事件委托的事件有
click、mousedown、mouseup、keydown、keyup、keypress
等。需要注意的是,虽然mouseover 和 mouseout
事件也会发生事件冒泡,但处理起来非常麻烦,所以不推荐在mouseover 和 mouseout
事件中使用事件委托。另外,对于不会发生事件冒泡的事件(例如
load、unload、abort、focus、blur
等),则无法使用事件委托。
# 事件冒泡与捕获
当一个事件发生在具有父元素的元素上时,浏览器运行2+1个阶段阶段:
- 捕获阶段:浏览器检查元素的最外层祖先,是否在捕获阶段中注册了一个onclick事件处理程序,如果是,则运行它。然后,它移动到中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。
- 冒泡阶段:浏览器检查实际点击的元素是否在冒泡阶段中注册了一个onclick事件处理程序,如果是,则运行它。然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达元素。
- 目标阶段:实际点击target。
<div id="parent" style="background-color: pink">
<h2>Parent</h2>
| /\<br>
\/ |<br>
<div id="son" style="background-color: green">
<h3>Son</h3>
| /\<br>
\/ |<br>
<div id="target" style="background-color: red">
<h4>Target</h4>
</div>
</div>
</div>
<script type="text/javascript">
var parent = document.getElementById("parent");
var son = document.getElementById("son");
var target = document.getElementById("target");
// parent.addEventListener("click", function (e) {
// console.log("冒泡-parent");
// }, false);
// son.addEventListener("click", function (e) {
// console.log("冒泡-son");
// }, false);
// target.addEventListener("click", function (e) {
// console.log("冒泡-target");
// }, false);
parent.addEventListener("click", function (e) {
console.log("捕获-parent");
}, true);
son.addEventListener("click", function (e) {
console.log("捕获-son");
}, true);
target.addEventListener("click", function (e) {
console.log("捕获-target");
}, true);
</script>
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
阻止冒泡
- 使用stopPropagation()方法,可以阻止冒泡,但无法阻止同一事件的其他监听函数被调用,也就是说给同一事件添加的不同监听器不会被阻止;
<button id="btn" style="width: 200px;">触发</button>
<script type="text/javascript">
var btn = document.getElementById('btn');
btn.addEventListener('click',function (e){
e = e || event;
// 可以阻止冒泡,但无法阻止同一事件的其他监听函数被调用
e.stopPropagation();
this.innerHTML = '修改了';
})
//使用stopPropagation(),该函数还会执行
btn.addEventListener('click',function (e){
e = e || event;
this.style.backgroundColor = 'lightblue';
})
//使用stopPropagation(),该函数不执行
document.body.addEventListener('click',function (){
alert('body');
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- stopImmediatePropagation()方法不仅可以取消事件的进一步捕获或冒泡,而且可以阻止同一个事件的其他监听函数被调用,无返回值;
<button id="btn" style="width: 200px;">触发</button>
<script type="text/javascript">
var btn = document.getElementById('btn');
// 可以阻止冒泡,但无法阻止同一事件的其他监听函数被调用
btn.addEventListener('click',function (e){
e = e || event;
e.stopImmediatePropagation()
this.innerHTML = '修改了';
})
// 使用stopImmediatePropagation()方法,该函数不执行
btn.addEventListener('click',function (e){
e = e || event;
this.style.backgroundColor = 'lightblue';
})
// 使用stopImmediatePropagation()方法,该函数不执行
document.body.addEventListener('click',function (){
alert('body');
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# js37双向绑定
请补全JavaScript代码,要求如下:
- 监听对象属性的变化
- 当"person"对象属性发生变化时,页面中与该属性相关的数据同步更新
- 将输入框中的值与"person"的"weight"属性绑定且当输入框的值发生变化时,页面中与该属性相关的数据同步更新
注意:
- 必须使用Object.defineProperty实现且触发set方法时更新视图
- 必须使用DOM0级标准事件(oninput)
- 可以使用预设代码"_render"函数
<style>
ul {
list-style: none;
}
</style>
<input type="text">
<ul></ul>
<script>
var ul = document.querySelector('ul');
var person = { sex: '男', age: '25', name: '王大锤', height: 28, weight: 32 };
var inp = document.querySelector('input');
inp.value = person.weight;
const _render = () => {
var str = `<li>姓名:<span>${person.name}</span></li>
<li>性别:<span>${person.sex}</span></li>
<li>年龄:<span>${person.age}</span></li>
<li>身高:<span>${person.height}</span></li>
<li>体重:<span>${person.weight}</span></li>`
ul.innerHTML = str;
inp.value = person.weight;
}
_render(ul);
// 补全代码
function Observer(target) {
if (typeof target !== 'object' || target == null) {
return target
}
for(let key in target) {
defineProperty(target, key, target[key])
}
}
function defineProperty(target, key, value) {
Object.defineProperty(person, key, {
get: function() {
return value
},
set: function(newValue) {
if (newValue != value) {
value = newValue
_render(ul);
}
}
})
}
Observer(person)
inp.oninput = function() {
person.weight = this.value
}
// 这里是要监听全部属性
// let value = person.weight
// // 注意:这里对象的key值要用引号引起来,是字符串
// Object.defineProperty(person, 'weight', {
// get: function() {
// return value
// },
// set: function(newValue) {
// if (newValue != value) {
// value = newValue
// _render(ul);
// }
// }
// })
// inp.oninput = function() {
// person.weight = this.value
// }
person.age = 44;
var spans = document.querySelectorAll('span');
var result = spans[2].innerText == 44;
console.log(result);
</script>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# js38高频数据类型
请补全JavaScript代码,要求找到参数数组中出现频次最高的数据类型,并且计算出出现的次数,要求以数组的形式返回。 注意:
- 基本数据类型之外的任何引用数据类型皆为"object"
- 当多种数据类型出现频次相同时将结果拼接在返回数组中,出现次数必须在数组的最后
输入:
__findMostType([0,0,'',''])
输出:
['number','string',2]或['string','number',2]
const _findMostType = array => {
// 补全代码
const map = new Map()
let max = 0 // 同时记录最大值
array.forEach(item => {
let key = typeof item
if (map.has(key)) {
map.set(key, map.get(key) + 1)
} else {
map.set(key, 1)
}
if(map.get(key) > max) {
max = map.get(key)
}
})
const arr = []
for(let key of map.keys()) {
if (map.get(key) == max) {
arr.push(key)
}
}
return [...arr, max]
}
console.log(_findMostType([0,0,'','']))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# js39字体高亮
请补全JavaScript代码,实现一个搜索字体高亮的效果。要求如下:
- 在input框中输入要搜索的内容,当点击查询按钮时,被搜索的字体样式变为加粗,背景色变为'yellow'
- 重新输入搜索文字,点击查询按钮时,去掉上一次的搜索效果,高亮显示效果只加在本次搜索文字上
- 如果搜索不到相关内容,清除之前的效果
注意:
- 需要加粗的文字请使用b标签包裹
- 必须使用DOM0级标准事件(onclick)
使用replaceAll通不过
<input type="text">
<button style="margin-right: 80px">查询</button>
<div class="text" style="margin-top: 70px">
牛客网隶属于北京牛客科技有限公司,牛客网成立于 2014 年 9 月,是以科技和创新驱动的教育科技公司。牛客网坚持以前沿技术服务于技术、以人工智能和大数据提升学习效率,专注探索在线教育创新模式,致力于为技术求职者提供能力提升解决方案,同时为企业级用户提供更高效的招聘解决方案,并为二者搭建桥梁,构建从学习���职业的良性生态圈。
发展至今,牛客网在技术类求职备考、社群交流、企业招聘服务等多个垂直领域影响力均在行业中遥遥领先,产品矩阵包括IT题库、在线编程练习、线上课程、交流社区、竞赛平台、笔面试服务、ATS系统等,用户覆盖全国高校百万IT学习者并在高速增长中,同时也为京东、百度、腾讯、滴滴、今日头条、华为等200多家企业提供校园招聘、编程竞赛等线上服务,并收获良好口碑。
</div>
<script>
var text = document.querySelector(".text");
var search = document.querySelector("input");
const btn = document.querySelector("button");
const original = text.innerText
btn.onclick = () => {
// 补全代码
textStr = original
searchStr = search.value
// textStr = textStr.replaceAll(searchStr, `<b style="background-color:yellow">${searchStr}</b>`)
textStr = textStr.replace(new RegExp(searchStr,'g'), `<b style="background-color:yellow">${searchStr}</b>`)
text.innerHTML = textStr
}
search.value = '牛客';
btn.onclick();
var result = document.querySelectorAll('b').length == 5;
console.log(result)
</script>
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
# js40虚拟DOM
请补全JavaScript代码,要求将对象参数转换为真实的DOM结构并返回。 注意:
- tag为标签名称、props为属性、children为子元素、text为标签内容
<script>
var vnode = {
tag: 'ul',
props: {
class: 'list'
},
text: '',
children: [
{
tag: "li",
props: {
class: "item"
},
text: '',
children: [
{
tag: undefined,
props: {},
text: '牛客网',
children: []
}
]
},
{
tag: "li",
props: {},
text: '',
children: [
{
tag: undefined,
props: {},
text: 'nowcoder',
children: []
}
]
}
]
}
const vnode2 = {
tag: "li",
props: {
class: "item"
},
text: '',
children: [
{
tag: undefined,
props: {},
text: '牛客网',
children: []
}
]
}
const _createElm = vnode => {
// 补全代码
// 判断是不是空标签
if (vnode.tag == undefined) {
return vnode.text
}
let tag_dom
if (vnode && vnode.tag) {
// 创建DOM元素
tag_dom = document.createElement(vnode.tag)
// 添加自定义属性或者内置属性
if (vnode.props) {
const prop_keys = Object.keys(vnode.props)
for (let i = 0; i < prop_keys.length; i++) {
tag_dom.setAttribute(prop_keys[i], vnode.props[prop_keys[i]])
}
}
// 添加文本
if (vnode.text != '') {
tag_dom.innerText = vnode.text
}
// 添加子节点
if (vnode.children) {
for(let i = 0; i < vnode.children.length; i++) {
let zi_dom = _createElm(vnode.children[i])
if (typeof zi_dom == 'string') {
tag_dom.innerText = zi_dom
} else {
tag_dom.appendChild(zi_dom)
}
}
}
}
return tag_dom
}
console.log(_createElm(vnode))
</script>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 操作自定义属性
element.getAttribute(“属性名”)
:主要获取我们自定义属性,当然也可以获取内置属性。
element.setAttribute(“属性名”,“值”)
:- 主要用来设置/添加自定义属性,也可以设置内置属性;
- 如果指定的属性不存在,则向 DOM 元素添加新属性。
- 如果指定的属性已经存在,则更新 DOM 元素的属性值。
element.removeAttribute(“属性名”)
:可以移除内置属性,也可以移除自定义属性。
setAttributeNode
:给元素设置属性或者修改元素属性
setAttributeNode
方法的功能类似于setAttribute
方法。唯一的区别是setAttributeNode
方法将Attr
节点作为参数。let divElement = document.getElementById("div"); let orderAttrNode = document.createAttribute('order'); orderAttrNode.value="2"; divElement.setAttributeNode(orderAttrNode);
1
2
3
4
5
# 创建元素
document.createElement("标签名")
创建元素
var oInput = document.createElement("input"); oInput.id = "submit"; oInput.type = "button"; oInput.value = "登录"; document.body.appendChild(oInput);
1
2
3
4
5
document.createTextNode("文本内容")
- 创建文本节点
# 插入元素
fatherdom.appendChild( insertdom )
appendChild()
把一个新元素插入到父元素的内部子元素的“末尾”// A表示父元素,B表示动态创建好的新元素 A.appendChild(B);
1
2
fatherdom.insertBefore( insertdom,chosendom )
insertBefore()
方法将一个新元素插入到父元素中的某一个子元素“之前”// A表示父元素,B表示新子元素。ref表示指定子元素,A.insertBefore(B,ref)则表示在ref之前插入B A.insertBefore(B,ref);
1
2
insertAdjacentHTML(position, text)
:- 把元素插入到指定位置
- position:
- beforebegin - 插入到当前元素的前面,即开始标签之前
- afterbegin - 作为当前元素的子元素,插入到所有子元素之前,即开始标签之后
- beforeend - 作为当前元素的子元素,插入到所有子元素之后,即结束标签之前
- afterend - 插入到当前元素的后面,即结束标签之后
- text:
- 是要被解析为HTML或XML,并插入到DOM树中的字符串。
# 删除和修改元素
remove()
:删除当前节点元素replaceWith()
:使用一个元素替换当前元素
# js41 dom结点查找
查找两个节点的最近的一个共同父节点,可以包括节点自身
oNode1 和 oNode2 在同一文档中,且不会为相同的节点
function commonParentNode(oNode1, oNode2) {
let node = oNode1
while(true) {
if (node.contains(oNode2)) {
return node
}
node = node.parentNode
}
}
const parent = document.querySelector(".parent")
const li = document.querySelector(".li")
const span = document.querySelector(".span")
console.log(commonParentNode(parent, span))
2
3
4
5
6
7
8
9
10
11
12
13
DOM(文档对象模型)可以将任何HTML、XML文档描绘成一个多层次的节点树。所有的页面都表现为以一个特定节点为根节点的树形结构。html文档中根节点为document节点。
所有节点都有nodeType属性,代表节点的不同类型,通过nodeType属性可以来判断节点的类型。经常使用的节点主要有以下几种类型:
Element类型(元素节点):nodeType值为 1
Text类型(文本节点):nodeType值为 3
Comment类型(注释节点):nodeType值为 8
Document类型(document节点):nodeType值为 9;其规定的一些常用的属性有
document.body document.head 分别为HTML中的
<body>``<head>
document.documentElement为
<html>
标签
所有的节点都有 hasChildNodes()方法 判断有无子节点 有一个或多个子节点时返回true
通过一些属性可以来遍历节点树:
parentNode
//获取所选节点的父节点,最顶层的节点为#documentchildNodes
//获取所选节点的子节点们firstChild
//获取所选节点的第一个子节点lastChild
//获取所选节点的最后一个子节点nextSibling
//获取所选节点的后一个兄弟节点 列表中最后一个节点的nextSibling属性值为nullpreviousSibling
//获取所选节点的前一兄弟节点 列表中第一个节点的previousSibling属性值为null
# js43修改this指向
封装函数 f,使 f 的 this 指向指定的对象
输入:
无
输出:
无
// fn.bind: 不会立即调用,而是返回一个绑定后的新函数。
function bindThis(f, oTarget) {
return f.bind(oTarget)
}
// fn.call:立即调用,返回函数执行结果,this指向第一个参数,后面可有多个参数,并且这些都是fn函数的参数。
function bindThis(f, oTarget) {
return function() {
return f.call(oTarget, ...arguments)
}
}
// fn.apply:立即调用,返回函数的执行结果,this指向第一个参数,第二个参数是个数组,这个数组里内容是fn函数的参数。
function bindThis(f, oTarget) {
return function() {
return f.apply(oTarget, arguments)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 需要立即调用使用
call
/apply
- 要传递的参数不多,则可以使用
fn.call(thisObj, arg1, arg2 ...)
- 要传递的参数很多,则可以用数组将参数整理好调用
fn.apply(thisObj, [arg1, arg2 ...])
- 不需要立即执行,而是想生成一个新的函数长期绑定某个函数给某个对象使用,使用
const newFn = fn.bind(thisObj); newFn(arg1, arg2...)
# js44根据包名,在指定空间中创建对象
根据包名,在指定空间中创建对象
namespace({a: {test: 1, b: 2}}, 'a.b.c.d')
输出:
{a: {test: 1, b: {c: {d: {}}}}}
function namespace(oNamespace, sPackage) {
let arr = sPackage.split('.')
let temp = oNamespace
for (let i = 0; i < arr.length; i++) {
let key = arr[i]
if (temp.hasOwnProperty(key) && typeof temp[key] == 'object') {
temp = temp[key]
}else {
temp[key] = {}
temp = temp[key]
}
}
}
function namespace2(oNamespace, sPackage) {
let temp = oNamespace
sPackage.split('.').forEach(item => {
temp = temp[item] = Object.assign({}, temp[item])
})
}
function namespace3(oNamespace, sPackage) {
sPackage.split('.').reduce((currentObj,key) => {
if (typeof currentObj[key] != 'object') {
currentObj[key] = {}
}
return currentObj[key]
}, oNamespace)
return oNamespace
}
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
# js45数组去重
为 Array 对象添加一个去除重复项的方法
输入:[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN]
输出:[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a']
2
Array.prototype.uniq = function () {
const set = new Set(this)
return [...set]
}
// 利用Array.from()
Array.prototype.uniq2 = function () {
const set = new Set([...this])
return Array.from(new Set(this))
}
// 利用set去重
Array.prototype.uniq3 = function () {
let arr = this
let res = []
let set = new Set()
for(let i = 0; i < arr.length; i++){
if (!set.has(arr[i])) {
res.push(arr[i])
set.add(arr[i])
}
}
return res
}
// 利用reduce去重
Array.prototype.uniq4 = function () {
let result = this.reduce((pre, curr) => {
if (pre.includes(curr)) {
return pre
}else {
return pre.concat(curr)
}
}, [])
return result
}
arr = [false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN]
console.log(arr.uniq4())
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
# JS46 斐波那契数列
用 JavaScript 实现斐波那契数列函数,返回第n个斐波那契数。 f(1) = 1, f(2) = 1 等
// 递归
function fibonacci(n) {
if (n == 1 || n == 2) {
return 1
}else {
return fibonacci(n-1) + fibonacci(n-2)
}
}
function fibonacci2(n) {
let a = 1, b = 1
for (let i = 1; i <= n; i++) {
if (i > 2) {
let temp = b
b = a + b
a = temp
}
}
return b
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# js47时间格式化输出
按所给的时间格式输出指定的时间 格式说明 对于 2014.09.05 13:14:20 yyyy: 年份,2014 yy: 年份,14 MM: 月份,补满两位,09 M: 月份, 9 dd: 日期,补满两位,05 d: 日期, 5 HH: 24制小时,补满两位,13 H: 24制小时,13 hh: 12制小时,补满两位,01 h: 12制小时,1 mm: 分钟,补满两位,14 m: 分钟,14 ss: 秒,补满两位,20 s: 秒,20 w: 星期,为 ['日', '一', '二', '三', '四', '五', '六'] 中的某一个,本 demo 结果为 五
输入:
formatDate(new Date(1409894060000), 'yyyy-MM-dd HH:mm:ss 星期w')
输出:
2014-09-05 13:14:20 星期五
2
3
4
function formatDate(date, format) {
function addZero(val) {
if (val < 10) {
return '0' + val
}
return val
}
let obj = {
'yyyy': date.getFullYear(),
'yy': date.getFullYear()%100,
'MM': addZero(date.getMonth() + 1),
'M': date.getMonth() + 1,
'dd': addZero(date.getDate()),
'd': date.getDate(),
'HH': addZero(date.getHours()),
'H': date.getHours(),
'hh': addZero(date.getHours() % 12),
'h': date.getHours() % 12,
'mm': addZero(date.getMinutes()),
'm': date.getMinutes(),
'ss': addZero(date.getSeconds()),
's': date.getSeconds(),
'w': function () {
arr = ['日', '一', '二', '三', '四', '五', '六']
return arr[date.getDay()]
}()
}
for(let key in obj) {
format = format.replace(key, obj[key])
}
return format
}
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
# 日期函数方法
new Date()
创建日期对象var d = new Date(); var d = new Date(milliseconds); // 参数为毫秒 var d = new Date(dateString); var d = new Date(year, month, day, hours, minutes, seconds, milliseconds);
1
2
3
4
d.getFullYear()
获取年份d.getMonth() + 1
获取月份d.getDate()
获取日d.getHours()
获取小时d.getMinutes()
获取分钟d.getSeconds()
获取秒d.getDay()
获取星期 值为0~6,其中0表示星期日,1表示星期一,...,6表示星期六。
# 获取时间戳
Date.now()
获得当前的时间戳console.log(Date.now()) //1642471441587
1
Date.parse()
将字符串或者时间对象直接转化成时间戳Date.parse(new Date()) //1642471535000 Date.parse("2022/1/18 10:05") //1642471500000
1
2
valueOf()
通过valueOf()函数返回指定对象的原始值获得准确的时间戳值:(new Date()).valueOf() //1642471624512
1
getTime()
通过原型方法直接获得当前时间的毫秒值new Date().getTime() //1642471711588
1
Number
将时间对象转化为一个number类型的数值,即时间戳Number(new Date()) //1642471746435
1
# 总结
方法 | 描述 |
---|---|
getDate() | 从 Date 对象返回一个月中的某一天 (1 ~ 31)。 |
getDay() | 从 Date 对象返回一周中的某一天 (0 ~ 6)。 |
getFullYear() | 从 Date 对象以四位数字返回年份。 |
getHours() | 返回 Date 对象的小时 (0 ~ 23)。 |
getMilliseconds() | 返回 Date 对象的毫秒(0 ~ 999)。 |
getMinutes() | 返回 Date 对象的分钟 (0 ~ 59)。 |
getMonth() | 从 Date 对象返回月份 (0 ~ 11)。 |
getSeconds() | 返回 Date 对象的秒数 (0 ~ 59)。 |
getTime() | 返回 1970 年 1 月 1 日至今的毫秒数。 |
getTimezoneOffset() | 返回本地时间与格林威治标准时间 (GMT) 的分钟差。 |
getUTCDate() | 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。 |
getUTCDay() | 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。 |
getUTCFullYear() | 根据世界时从 Date 对象返回四位数的年份。 |
getUTCHours() | 根据世界时返回 Date 对象的小时 (0 ~ 23)。 |
getUTCMilliseconds() | 根据世界时返回 Date 对象的毫秒(0 ~ 999)。 |
getUTCMinutes() | 根据世界时返回 Date 对象的分钟 (0 ~ 59)。 |
getUTCMonth() | 根据世界时从 Date 对象返回月份 (0 ~ 11)。 |
getUTCSeconds() | 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。 |
getYear() | 已废弃。 请使用 getFullYear() 方法代替。 |
parse() | 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。 |
setDate() | 设置 Date 对象中月的某一天 (1 ~ 31)。 |
setFullYear() | 设置 Date 对象中的年份(四位数字)。 |
setHours() | 设置 Date 对象中的小时 (0 ~ 23)。 |
setMilliseconds() | 设置 Date 对象中的毫秒 (0 ~ 999)。 |
setMinutes() | 设置 Date 对象中的分钟 (0 ~ 59)。 |
setMonth() | 设置 Date 对象中月份 (0 ~ 11)。 |
setSeconds() | 设置 Date 对象中的秒钟 (0 ~ 59)。 |
setTime() | setTime() 方法以毫秒设置 Date 对象。 |
setUTCDate() | 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。 |
setUTCFullYear() | 根据世界时设置 Date 对象中的年份(四位数字)。 |
setUTCHours() | 根据世界时设置 Date 对象中的小时 (0 ~ 23)。 |
setUTCMilliseconds() | 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。 |
setUTCMinutes() | 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。 |
setUTCMonth() | 根据世界时设置 Date 对象中的月份 (0 ~ 11)。 |
setUTCSeconds() | setUTCSeconds() 方法用于根据世界时 (UTC) 设置指定时间的秒字段。 |
setYear() | 已废弃。请使用 setFullYear() 方法代替。 |
toDateString() | 把 Date 对象的日期部分转换为字符串。 |
toGMTString() | 已废弃。请使用 toUTCString() 方法代替。 |
toISOString() | 使用 ISO 标准返回字符串的日期格式。 |
toJSON() | 以 JSON 数据格式返回日期字符串。 |
toLocaleDateString() | 根据本地时间格式,把 Date 对象的日期部分转换为字符串。 |
toLocaleTimeString() | 根据本地时间格式,把 Date 对象的时间部分转换为字符串。 |
toLocaleString() | 根据本地时间格式,把 Date 对象转换为字符串。 |
toString() | 把 Date 对象转换为字符串。 |
toTimeString() | 把 Date 对象的时间部分转换为字符串。 |
toUTCString() | 根据世界时,把 Date 对象转换为字符串。实例:var today = new Date(); var UTCstring = today.toUTCString(); |
UTC() | 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。 |
# js48获取字符串的长度
如果第二个参数 bUnicode255For1 === true,则所有字符长度为 1否则如果字符 Unicode 编码 > 255 则长度为 2
输入:
'hello world, 牛客', false
输出:
17
function strLength(s, bUnicode255For1) {
if (bUnicode255For1) {
return s.length
}
let len = 0
for(let i = 0; i < s.length; i++) {
let c = s.charCodeAt(i)
if (c > 255) {
len += 2
}else {
len += 1
}
}
return len
}
console.log(strLength('hello world, 牛客', false))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# JS49邮箱字符串判断
判断输入是否是正确的邮箱格式
true表示格式正确
function isAvailableEmail(sEmail) {
let reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
return reg.test(sEmail)
}
function isAvailableEmail(sEmail) {
let reg=/^\w+@[\da-z\.-]+\.([a-z]+|[\u4E00-\u9FFF]+)$/
return reg.test(sEmail);
}
// 下面将这个正则,按照我的理解来给大家分析一下。
// 主要通过 @ 和. 将整体分为三部分:
// 1、^\w + : ^ 代表以后面的开头
// \w代表数字+字母+下划线
// + 代表匹配前面的表达式一次或多次
// 2、[ \da-z\.-]+ : [ ] 代表中括号表达式的开始
// \d 代表[0-9]的数字,取其一
// a-z 代表小写字母,取其一
// \. 代表匹配 .
// - 代表匹配 –
// 3、([a-z]+|[\u4E00-\u9FFF ]+)$: ()代表子表达的开始
// | 代表任选其一
// \u 代表Unicode编码
// 4E00-9FFF为中文字符编码区
// $ 代表以前面的表达式结束
console.log(isAvailableEmail('123@qq.com'))
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
# js正则表达式
创建对象
用字面量创建
let reg = /abc/
1RegExp构造函数
let reg = new RegExp('abc')
1
执行方法
test方法返回一个布尔值,表示当前模式是否能匹配参数字符串
reg.test('abc')
1exec方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null。
let reg1 = /it/ let name = "itabcdefg"; let result2 = reg1.exec(name); console.log(result2); // ['it', index: 0, input: 'itbaizhan'] 创建正则: 1. 字面量:开始和结尾是斜杠 2. 构造函数:new关键字 执行正则: 1. test(); 2. exec(); 0:"it" : 正则的匹配规则 index:代表从哪个位置开始匹配 input:代表匹配的原字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
# js50计数
统计数组 arr 中值等于 item 的元素出现的次数
输入:
[1, 2, 4, 4, 3, 4, 3], 4
输出:
3
function count(arr, item) {
return arr.reduce((pre, curr) => {
if (curr == item) {
pre++
}
return pre
}, 0)
}
2
3
4
5
6
7
8
function count2(arr, item) {
return arr.filter(curr => {
return curr == item
}).length
}
2
3
4
5
# JS51 查找重复元素
找出数组 arr 中重复出现过的元素(不用考虑返回顺序)
输入:
[1, 2, 4, 4, 3, 3, 1, 5, 3]
输出:
[1, 3, 4]
// 利用map来存储
function duplicates(arr) {
let result = []
let map = new Map()
arr.forEach(item => {
if (map.has(item)) {
map.set(item, map.get(item) + 1)
}else {
map.set(item, 1)
}
})
// 也可以用forEach
for(let key of map.keys()) {
if (map.get(key) > 1) {
result.push(key)
}
}
return result
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function duplicates2(arr) {
let result = []
arr.forEach(item => {
if (arr.indexOf(item) != arr.lastIndexOf(item) && result.indexOf(item) == -1) {
result.push(item)
}
})
return result
}
2
3
4
5
6
7
8
9
function duplicates3(arr) {
const set = new Set()
let result = new Set()
arr.forEach(item => {
if (set.has(item)) {
result.add(item)
}else {
set.add(item)
}
})
return [...result]
}
2
3
4
5
6
7
8
9
10
11
12
# js52计时器
实现一个打点计时器,要求 1、从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1 2、返回的对象中需要包含一个 cancel 方法,用于停止定时操作 3、第一个数需要立即输出
function count(start, end) {
console.log(start)
let timer = setInterval(() => {
if (start < end) {
console.log(++start)
} else {
clearInterval(timer)
}
}, 100)
return {
cancel() {
clearInterval(timer)
}
}
}
count(1, 10)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 定时器
- setTimeout():指定时间后执行一段代码(延迟执行)。
- setInterval():每隔一段时间执行一段代码(间隔执行)。
let t = setTimeout(() => {
console.log('我将在两秒后输出')
}, 2 * 1000)
let t2 = setTimeout(() => {
console.log('字符100串被强制转换成数字 1000(隐含类型强制转换), 1秒字符串将会变为0')
}, "1000")
// 返回值 timeoutID 是一个正整数,表示由 setTimeout() 调用创建的定时器的编号。这个值可以传递给 clearTimeout() 来取消该定时器。
console.log(t2)
clearTimeout(t2)
2
3
4
5
6
7
8
9
let t3 = setInterval(() => {
console.log('间隔执行')
}, 1000)
setTimeout(() => {
clearInterval(t3)
}, 3000)
2
3
4
5
6
7
# js53流程控制
实现 fizzBuzz 函数,参数 num 与返回值的关系如下: 1、如果 num 能同时被 3 和 5 整除,返回字符串 fizzbuzz 2、如果 num 能被 3 整除,返回字符串 fizz 3、如果 num 能被 5 整除,返回字符串 buzz 4、如果参数为空或者不是 Number 类型,返回 false 5、其余情况,返回参数 num
输入:
15
输出:
fizzbuzz
function fizzBuzz(num) {
if (num == null || typeof num != 'number') {
return false
}
if (num % 3 === 0 && num % 5 === 0) {
return 'fizzbuzz'
} else if (num % 3 === 0) {
return 'fizz'
} else if (num % 5 === 0) {
return 'buzz'
} else {
return num
}
}
console.log(fizzBuzz(15))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# js54函数传参
将数组 arr 中的元素作为调用函数 fn 的参数
输入:
function (greeting, name, punctuation) {return greeting + ', ' + name + (punctuation || '!');}, ['Hello', 'Ellie', '!']
输出:
Hello, Ellie!
function argsAsArray(fn, arr) {
return fn(...arr)
}
function argsAsArray2(fn, arr) {
return fn.call(this, ...arr)
}
function argsAsArray3(fn, arr) {
return fn.apply(this, arr)
}
const res = argsAsArray3( function (greeting, name, punctuation) {
return greeting + ', ' + name + (punctuation || '!');
}, ['Hello', 'Ellie', '!'])
console.log(res)
2
3
4
5
6
7
8
9
10
11
12
13
# js55函数的上下文
将函数 fn 的执行上下文改为 obj 对象
输入:
function () {return this.greeting + ', ' + this.name + '!!!';}, {greeting: 'Hello', name: 'Rebecca'}
输出:
Hello, Rebecca!!!
function speak(fn, obj) {
return fn.call(obj)
}
function speak(fn, obj) {
return fn.apply(obj)
}
const res = speak(function () {
return this.greeting + ', ' + this.name + '!!!';
}, {greeting: 'Hello', name: 'Rebecca'})
console.log(res)
2
3
4
5
6
7
8
9
10
# apply ,call , bind的区别
# apply和call
call() 与apply()只有一个区别,就是 call()
方法接受的是一个参数列表,而 apply()
方法接受的是一个包含多个参数的数组。 call(); 传递参数类型 直接返回调用结果.
call
和apply
的唯一区别就是,call
需要一个个的传可选参数,而apply
只需要传一个数组的可选参数。
普通函数的this默认指向Window。所以下面输出==undefined 作者: undefined.==
const book = {
title: '西游记',
author: '吴承恩',
}
function summary() {
console.log(`${this.title} 作者: ${this.author}.`)
}
summary() //undefined 作者: undefined
summary.call(book) // 西游记 作者: 吴承恩.
summary.apply(book) // 西游记 作者: 吴承恩.
function longerSummary(genre, year) {
console.log(
`${this.title} 作者: ${this.author}. 它是一个 ${genre} 在 ${year} 发表.`
)
}
longerSummary.call(book, '小说', 1999) // 西游记 作者: 吴承恩. 它是一个 小说 在 1999 发表.
longerSummary.apply(book, ['小说', 1999]) // 西游记 作者: 吴承恩. 它是一个 小说 在 1999 发表.
// longerSummary.apply(book, '小说', 1999) // this指向问题.html:125 Uncaught TypeError: CreateListFromArrayLike called on non-object
longerSummary.apply(book, ['小说', 1999]) // 西游记 作者: 吴承恩. 它是一个 小说 在 1999 发表.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# bind
call
和apply
都是一次性使用的方法 -- 如果你调用带有this
上下文的方法,它将含有此上下文,但是原始的函数依旧没改变。
有时候,你可能需要重复地使用方法来调用另一个对象的上下文,所以,在这种场景下你应该使用bind
方法来创建一个显示调用this
的全新函数。
const newSummary = summary.bind(book)
newSummary() // 西游记 作者: 吴承恩.
2
在这个例子中,每次你调用newSummary
,它都会返回绑定它的原始this
值。尝试绑定一个新的this
上下文将会失败。因此,你始终可以信任绑定的函数来返回你期待的this
值。
const newSummary = summary.bind(book)
newSummary() // 西游记 作者: 吴承恩.
const book2 = {
title: '1984',
author: 'George Orwell',
}
const newSummary2 = newSummary.bind(book2) // 并没有绑定成功
newSummary2(); // 西游记 作者: 吴承恩.
2
3
4
5
6
7
8
9
# 比较
相同点:
三者都是用来改变函数的上下文,也就是this
指向的。
不同点:
fn.bind
: 不会立即调用,而是返回一个绑定后的新函数。
fn.call
:立即调用,返回函数执行结果,this
指向第一个参数,后面可有多个参数,并且这些都是fn
函数的参数。
fn.apply
:立即调用,返回函数的执行结果,this
指向第一个参数,第二个参数是个数组,这个数组里内容是fn
函数的参数。
# 应用场景
需要立即调用使用
call
/apply
要传递的参数不多,则可以使用
fn.call(thisObj, arg1, arg2 ...)
要传递的参数很多,则可以用数组将参数整理好调用
fn.apply(thisObj, [arg1, arg2 ...])
不需要立即执行,而是想生成一个新的函数长期绑定某个函数给某个对象使用,使用
const newFn = fn.bind(thisObj); newFn(arg1, arg2...)
# js56返回函数
实现函数 functionFunction,调用之后满足如下条件: 1、返回值为一个函数 f 2、调用返回的函数 f,返回值为按照调用顺序的参数拼接,拼接字符为英文逗号加一个空格,即 ', ' 3、所有函数的参数数量为 1,且均为 String 类型
输入:
functionFunction('Hello')('world')
输出:
Hello, world
// 闭包
function functionFunction(str) {
return function() {
return str + ", " + arguments[0]
}
// return function(argument) {
// return str + ", " + argument
// }
}
//
function functionFunction2(str) {
let arr = []
function fn(str){
arr.push(str)
return arr.join(', ')
}
fn(str)
return fn
}
// 柯里化
function functionFunction3(str) {
// 字符串拼接函数
function strAdd() {
return Array.prototype.join.call(arguments,', ')
}
// 柯里化工具函数
function curry(fn, argLength) {
return function curried(...args) {
if(args.length >= argLength) {
return fn.apply(this, args)
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2))
}
}
}
}
// 将字符串函数柯里化,目标参数长度为2(也可以是其他长度)
// 并进行初次调用
return curry(strAdd, 2)(str)
}
console.log(functionFunction3('Hello')('world'))
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
# 柯里化
请定义一个 JavaScript 方法,实现如下输出:
sum(2, 3) // 5
sum(2)(3) // 5
2
# js57使用闭包
实现函数 makeClosures,调用之后满足如下条件: 1、返回一个函数数组 result,长度与 arr 相同
2、运行 result 中第 i 个函数,即 resulti,结果与 fn(arr[i]) 相同
示例:
var arr = [1,2,3];
var fn = function (x) {
return x * x;
}
var result = makeClosures(arr,fn);
(result[1]() === 4) === (fn(arr[1]) === 4) === true
2
3
4
5
6
function makeClosures(arr, fn) {
const result = []
arr.forEach((item, i) => {
result[i] = fn.bind(this, item)
})
return result
}
function makeClosures(arr, fn) {
return arr.map(item => {
return function() {
return fn(item)
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# js58二次封装函数
已知函数 fn 执行需要 3 个参数。请实现函数 partial,调用之后满足如下条件: 1、返回一个函数 result,该函数接受一个参数 2、执行 result(str3) ,返回的结果与 fn(str1, str2, str3) 一致
输入:
var sayIt = function(greeting, name, punctuation) { return greeting + ', ' + name + (punctuation || '!'); }; partial(sayIt, 'Hello', 'Ellie')('!!!');
输出:
Hello, Ellie!!!
function partial(fn, str1, str2) {
return function (arg) {
return fn(str1, str2, arg)
}
}
// 偏函数
function partial2(fn, str1, str2) {
return function() {
// 参数0代表从0开始截取
let args = Array.prototype.slice.call(arguments,0)
return fn.apply(this, [str1, str2].concat(args))
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# js59使用arguments
函数 useArguments 可以接收 1 个及以上的参数。请实现函数 useArguments,返回所有调用参数相加后的结果。本题的测试参数全部为 Number 类型,不需考虑参数转换。
输入:
1, 2, 3, 4
输出:
10
console.log(typeof arguments) // object
console.log(Array.isArray(arguments)) // false
// 解构数组
function useArguments() {
const sum = [...arguments].reduce((prev, curr) => {
return prev + curr
})
return sum
}
// Array.from
function useArguments2() {
const arr = Array.from(arguments)
const sum = arr.reduce((prev, curr) => {
return prev + curr
})
return sum
}
// Array.prototype.slice.apply(argumentsm, 0) || [].slice.apply(argumentsm, 0)
function useArguments3() {
const arr = [].slice.apply(arguments)
const sum = arr.reduce((prev, curr) => {
return prev + curr
})
return sum
}
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
# 类数组转换为数组
Array.prototype.slice.call(arguments);
数组的slice()方法可以从已有数组中返回一个新数组,它可以接受两个参数arr.slice(start,end),第一个参数规定从何处开始选取,第二个参数表示从何处选取结束,如果不传参将返回原数组的一个副本,但该方法不会修改原数组,而是返回截取的新数组。
Array.prototype.splice.call(arguments, 0);
Array.prototype.concat.apply([], arguments);
Array.from(arguments);
# js61二次封装函数
实现函数 partialUsingArguments,调用之后满足如下条件: 1、返回一个函数 result 2、调用 result 之后,返回的结果与调用函数 fn 的结果一致 3、fn 的调用参数为 partialUsingArguments 的第一个参数之后的全部参数以及 result 的调用参数
function partialUsingArguments(fn) {
const args1 = [...arguments].slice(1)
return function() {
const args2 = [...arguments]
return fn.apply(this, args1.concat(args2))
}
}
(function () {
var a = 1;
var b = 2;
var c = 3;
var d = 4;
var test = function (first, second, third, forth) {
return first + second + third + forth;
};
console.log( partialUsingArguments(test, a, b)(c, d));
})()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# js62柯里化
已知 fn 为一个预定义函数,实现函数 curryIt,调用之后满足如下条件: 1、返回一个函数 a,a 的 length 属性值为 1(即显式声明 a 接收一个参数) 2、调用 a 之后,返回一个函数 b, b 的 length 属性值为 1 3、调用 b 之后,返回一个函数 c, c 的 length 属性值为 1 4、调用 c 之后,返回的结果与调用 fn 的返回值一致 5、fn 的参数依次为函数 a, b, c 的调用参数
本题与柯里化函数不同点在于 严格限制了每次返回的函数只能传入一个参数
// 严格限制了每次返回的函数只能传入一个参数
function curryIt(fn) {
const args = []
function curriedFn(arg) {
args.push(arg)
if(args.length >= fn.length) {
return fn.apply(this, args)
} else {
return function(args2) {
return curriedFn.call(this, args2)
}
}
}
return curriedFn
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function curryIt2(fn) {
let args = []
// 声明一个数组来累计参数,等到参数个数符合fn函数的参数个数的时候直接调fn函数结束这次函数调用
return result = function(arg) {
args.push(arg)
if(args.length >= fn.length) {
return fn.apply(this, args)
} else {
return result
}
}
}
2
3
4
5
6
7
8
9
10
11
12
function curryIt3(fn) {
return a => b => c => fn(a,b,c)
}
2
3
# js63模块化
完成函数 createModule,调用之后满足如下要求: 1、返回一个对象 2、对象的 greeting 属性值等于 str1, name 属性值等于 str2 3、对象存在一个 sayIt 方法,该方法返回的字符串为 greeting属性值 + ', ' + name属性值
// 注意,箭头函数没有this。如果把sayIt写成箭头函数,那么内部的this就会变成其所在作用域的调用者,
// 而对象的大括号不算做作用域,故就变成了window,就会出错。
function createModule(str1, str2) {
return {
greeting: str1,
name: str2,
sayIt: function() {
return this.greeting + ", " + this.name
}
}
}
var o = createModule('hello', 'matt');
o.name = 'katniss';
o.greeting = 'hi';
console.log(o.sayIt());
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# js64二进制转换
获取数字 num 二进制形式第 bit 位的值。注意: 1、bit 从 1 开始 2、返回 0 或 1 3、举例:2 的二进制为 10,第 1 位为 0,第 2 位为 1
输入:
128, 8
输出:
1
2
3
4
// toString
// Number.toString([radix]):
// 返回指定 Number 对象的字符串表示形式;
// radix指定要用于数字到字符串的转换的基数(从2到36)。如果未指定 radix 参数,则默认值为 10
function valueAtBit(num, bit) {
let num2String = num.toString(2)
return num2String[num2String.length - bit]
}
2
3
4
5
6
7
8
// 方法二 位运算
function valueAtBit2(num, bit) {
return (num >> (bit - 1)) & 1
}
2
3
4
&: 与 两个位都为1时,结果才为1 |: 或 两个位都为0时,结果才为0 ^: 异或 两个位相同为0,相异为1 ~: 取反 0变1,1变0 <<: 左移 各二进位全部左移若干位,高位丢弃,低位补0 '>>: 右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)
// 方法三 手动实现
function valueAtBit3(num, bit) {
let arr = []
while(num > 0) {
arr.push(num % 2)
num = Math.floor(num / 2)
}
return arr[bit - 1]
}
2
3
4
5
6
7
8
9
# 进制转换
parseInt()
: 将字符串转换成整数parseInt(string, radix)
1string要被解析的字符串。
可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。
如果省略该参数或其值为 0,则数字将以 10 为基础来解析。 如果它以 “0x” 或 “0X” 开头,将以 16 为基数。
如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。
toString()
:var x=110; alert(x); alert(x.toString(2)); // 1101110 alert(x.toString(8)); // 156 alert(x.toString(32)); // 3e alert(x.toString(16)); // 6e
1
2
3
4
5
6
# js65二进制转换
给定二进制字符串,将其换算成对应的十进制数字
输入:
'11000000'
输出:
192
// 手写实现
function base10(str) {
let result = 0
let count = 0
for(let i = str.length - 1; i >= 0; i --) {
result += parseInt(str[i]) * Math.pow(2, count)
count++
}
return result
}
2
3
4
5
6
7
8
9
10
function base102(str) {
return parseInt(str, 2)
}
2
3
# js66二进制转换
将给定数字转换成二进制字符串。如果字符串长度不足 8 位,则在前面补 0 到满8位。
输入:
65
输出:
01000001
function convertToBinary(num) {
// 通不过校验
// return num.toString(2).padStart(8, '0')
return ('0'.repeat(8) + num.toString(2)).slice(-8)
}
let result = function() { return convertToBinary(65) }
console.log(result())
2
3
4
5
6
7
# JS67乘法
求 a 和 b 相乘的值,a 和 b 可能是小数,需要注意结果的精度问题
输入:
3, 0.0001
输出:
0.0003
2
3
4
function multiply(a, b) {
// 获取小数后面的位数
function getAccuracy(num) {
const len = num.toString().indexOf('.')
return len === -1 ? 0 : num.toString().length - len - 1
}
const fixed = Math.max(getAccuracy(a) + getAccuracy(b))
return (a*b).toFixed(fixed)
}
console.log(multiply(3, 0.0001))
2
3
4
5
6
7
8
9
10
# js68改变上下文
将函数 fn 的执行上下文改为 obj,返回 fn 执行后的值
输入:
alterContext(function() {return this.greeting + ', ' + this.name + '!'; }, {name: 'Rebecca', greeting: 'Yo' })
输出:
Yo, Rebecca!
2
3
4
function alterContext(fn, obj) {
// call的参数是直接放进去,里面的参数用','隔开
// return fn.call(obj)
// apply的所有参数必须放在一个数组中传进去
// return fn.apply(obj)
// bind的参数除了返回的函数时数组之外,其他参数和call一样
return fn.bind(obj)()
}
2
3
4
5
6
7
8
# js69批量改变对象的属性
给定一个构造函数 constructor,请完成 alterObjects 方法,将 constructor 的所有实例的 greeting 属性指向给定的 greeting 变量。
输入:
var C = function(name) {this.name = name; return this;};
var obj1 = new C('Rebecca');
alterObjects(C, 'What\'s up'); obj1.greeting;
输出:
What's up
2
3
4
5
6
function alterObjects(constructor, greeting) {
// console.log(constructor.prototype)
constructor.prototype.greeting = greeting
}
var C = function(name) {
this.name = name;
return this;
};
var obj1 = new C('Rebecca');
alterObjects(C, 'What\'s up');
console.log(obj1.greeting)
2
3
4
5
6
7
8
9
10
11
# Js new一个函数和直接调用函数的区别 (opens new window)
没有带new,也就是普通的函数调用,所以若是函数本身没有返回值,普通的函数调用没有什么意义,不起作用,所以这个函数必须要有return
带new,new声明的是一个对象,像一般的对象一样拥有属性和值,也可以像对象一样方法调用,不需要写return;
# js70属性遍历
找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~) 1、返回数组,格式为 key: value 2、结果数组不要求顺序
输入:
var C = function() {this.foo = 'bar'; this.baz = 'bim';};
C.prototype.bop = 'bip';
iterate(new C());
输出:
["foo: bar", "baz: bim"]
2
3
4
5
6
function iterate(obj) {
let result = []
for(let key in obj) {
if(!obj.hasOwnProperty(key) && key in obj) {
// console.log('在原型上')
continue
}else {
result.push(key + ": " + obj[key])
}
}
return result
}
var C = function() {this.foo = 'bar'; this.baz = 'bim';};
C.prototype.bop = 'bip';
console.log(iterate(new C()));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 检测属性位于对象本身还是原型链
in 操作符
in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于对象本身还是其原型链 (opens new window)上。
function Person(name) { this.name = name } let obj = new Person('Tom') Person.prototype.gender = 'male' Person.prototype.code = 23 console.log("name" in obj) // true console.log('code' in obj) // true console.log('gender' in obj) // true
1
2
3
4
5
6
7
8
9
10
11
12obj.hasOwnProperty(prop)
hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性。function Person(name) { this.name = name } let obj = new Person('Tom') Person.prototype.gender = 'male' Person.prototype.code = 23 console.log(obj.hasOwnProperty('name')) // true console.log(obj.hasOwnProperty('code')) // false
1
2
3
4
5
6
7
8
9
10两者结合判断属性位于对象本身还是来自于其原型链
function Person(name) { this.name = name } let obj = new Person('Tom') Person.prototype.gender = 'male' Person.prototype.code = 23 // 判断一个属性是否在原型上 function propertyFormPrototype(obj, prop) { return !obj.hasOwnProperty(prop) && prop in obj } console.log(propertyFormPrototype(obj, 'name')) // false console.log(propertyFormPrototype(obj, 'code')) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
# js71判断是否包含数字
给定字符串 str,检查其是否包含数字,包含返回 true,否则返回 false
输入:
'abc123'
输出:
true
function containsNumber(str) {
let reg = /\d+/
return reg.test(str)
}
2
3
4
function containsNumber2(str) {
let reg = /[0-9]/g
return reg.test(str)
}
2
3
4
function containsNumber3(str) {
for(let i = 0; i < 10; i++) {
if(str.indexOf(i) != -1) {
return true
}
}
return false
}
2
3
4
5
6
7
8
function containsNumber4(str) {
// const arr = str.split('')
// const arr = [...str]
const arr = Array.from(str)
return arr.some(item => {
return !isNaN(item)
})
}
2
3
4
5
6
7
8
# 字符串转数组
split
split()方法用于将给定字符串拆分为字符串数组;该方法是使用参数中提供的指定分隔符将其分隔为子字符串,然后一个个传入数组中作为元素。
str. split(separator,limit) const arr = str.split('')
1
2参数:
- separator:可选。字符串或正则表达式,从该参数指定的地方分割 string Object。
- limit:可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。
扩展运算符...
扩展操作符
…
是ES6中引入的,将可迭代对象展开到其单独的元素中,所谓的可迭代对象就是任何能用for of循环进行遍历的对象。String 也是一个可迭代对象,所以也可以使用扩展运算符
...
将其转为字符数组const arr = [...str]
1Array.from()
Array.from()方法是javascript中的一个内置函数,它从给定的数组创建一个新的数组实例。对于字符串,字符串的每个字母表都会转换为新数组实例的元素;对于整数值,新数组实例simple将获取给定数组的元素。
Array.from(str) const arr = Array.from(str)
1
2
# js72检查重复字符串
给定字符串 str,检查其是否包含连续重复的字母(a-zA-Z),包含返回 true,否则返回 false
输入:
'rattler'
输出:
true
function containsRepeatingLetter(str) {
for(let i = 1; i < str.length; i++) {
if(str[i] === str[i-1] && /[a-zA-Z]/.test(str[i])) {
return true
}
}
return false
}
2
3
4
5
6
7
8
function containsRepeatingLetter2(str) {
// \1: 这是一个反向引用,它引用了第一个捕获组(即括号内的内容)。\1表示与第一个捕获组相同的内容。
// 因此,这里表示匹配前面捕获组所匹配的内容的重复出现。
return /([a-zA-Z])\1/.test(str)
}
2
3
4
5
# js73判断是否以元音字母结尾
给定字符串 str,检查其是否以元音字母结尾 1、元音字母包括 a,e,i,o,u,以及对应的大写 2、包含返回 true,否则返回 false
输入:
'gorilla'
输出:
true
function endsWithVowel(str) {
let reg = /[aeiouAEIOU]$/
return reg.test(str)
}
function endsWithVowel2(str){
values = 'aeiouAEIOU'.split('')
return values.indexOf(str[str.length - 1]) !== -1
}
2
3
4
5
6
7
8
# js74获取指定字符串
给定字符串 str,检查其是否包含 连续3个数字,请使用正则表达式实现。 1、如果包含,返回最先出现的 3 个数字的字符串 2、如果不包含,返回 false
输入:
'9876543'
输出:
987
function captureThreeNumbers(str) {
let reg = /(\d{3})/g
// if(str.match(reg) == null) {
// return false
// } else {
// return str.match(reg)[0]
// }
return (str.match(reg) && str.match(reg)[0]) || false
}
function captureThreeNumbers2(str) {
let reg = /\d{3}/
return reg.exec(str) ? reg.exec(str)[0] : false
}
console.log(captureThreeNumbers2('987654321'))
2
3
4
5
6
7
8
9
10
11
12
13
14
# match和exec的区别
exec是RegExp类的方法
match是String类的方法
exec 只会匹配第一个符合的字符串(意味着g对其不起作用),跟所有分组的反向引用
match 是否返回所有匹配的数组跟正则表达式里是否带着g有关系
match是字符串执行匹配正则表达式规则的方法,它的参数是正则表达,
而exec是正则表达式的方法,不是字符串的方法,它的参数是字符串。
在定义为非全局匹配时:
1、正则表达式无子表达式,exec和match执行的结果是一样,均返回第一个匹配的字符串内容;
2、正则表达式有子表示时,exec和match执行的结果是一样;
在定义为全局匹配时:
1、正则表达式无子表达式,exec和match执行,做存在多处匹配内容,则match返回的是多个元素数组;
2、正则表达式有子表示,exec和match执行的结果不一样,此时match将忽略子表达式,只查找全匹配正则表达式并返回所有内容
# js75判断是否符合指定格式
给定字符串 str,检查其是否符合如下格式 1、XXX-XXX-XXXX 2、其中 X 为 Number 类型
输入:
'800-555-1212'
输出:
true
function matchesPattern(str) {
let reg = /^\d{3}-\d{3}-\d{4}$/
return reg.test(str)
}
2
3
4
# js77颜色字符串转换
将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255, 255, 255) 转为 #ffffff
- rgb 中每个 , 后面的空格数量不固定
- 十六进制表达式使用六位小写字母
- 如果输入不符合 rgb 格式,返回原始输入
输入:
'rgb(255, 255, 255)'
输出:
#ffffff
function rgb2hex(sRGB) {
const reg = /^rgb\((.*)\)$/
if(!reg.test(sRGB)) {
return sRGB
}
const arr = sRGB.match(reg)[1].split(',').map(item => {
return item.trim()
})
if(arr.length !== 3) {
return arsRGB
}
let str = '#'
arr.forEach(item => {
str += getHex(item)
})
return str
}
function getHex(num) {
let str = ''
let n = parseInt(num)
if(n == 0) return '00'
while(n != 0) {
str += Hex(n % 16)
n = parseInt(n / 16)
}
if(str.length != 2) {
str = '0' + str
}
return str.split('').reverse().join('')
}
function Hex(num) {
switch(num) {
case 10: return 'a';
case 11: return 'b';
case 12: return 'c';
case 13: return 'd';
case 14: return 'e';
case 15: return 'f';
default: return num
}
}
// 方法2
function rgb2hex2(sRGB) {
try {
return eval(sRGB)
} catch(err) {
return sRGB
}
}
function rgb(r,g,b) {
let rH = r.toString(16)
let gH = g.toString(16)
let bH = b.toString(16)
rH = rH.length == 1 ? '0' + rH : rH
gH = gH.length == 1 ? '0' + gH : gH
bH = bH.length == 1 ? '0' + bH : bH
return '#' + rH + gH + bH
}
function abc() { return rgb2hex('rgb(255,255,100)') }
console.log(abc())
console.log(rgb2hex2('rgb(255,255,100)'))
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# js78 将字符串转换为驼峰格式
css 中经常有类似 background-image 这种通过 - 连接的字符,通过 javascript 设置样式的时候需要将这种样式转换成 backgroundImage 驼峰格式,请完成此转换功能
- 以 - 为分隔符,将第二个起的非空单词首字母转为大写
- -webkit-border-image 转换后的结果为 webkitBorderImage
输入:
'font-size'
输出:
fontSize
function cssStyle2DomStyle(sName) {
// 填写JavaScript
if(sName.startsWith('-')) {
sName = sName.slice(1)
}
let str = ''
for(let i = 0; i < sName.length; i++) {
if(sName[i] != '-') {
str += sName[i]
} else {
str += sName[i + 1].toUpperCase()
i++
}
}
return str
}
function cssStyle2DomStyle2(sName) {
return sName.replace(/^-/, '').replace(/-([a-z])/g,(_,$) => {
// _代表匹配到的字符串。比如font-size _就是-s
// $代表捕获组的内容
return $.toUpperCase()
})
}
// 另外一种实现思路,split('-')分开
console.log(cssStyle2DomStyle2('font-size'))
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
# js79购物车
HTML模块为一个简化版的购物车,tbody为商品列表,tfoot为统计信息,系统会随机在列表中生成一些初始商品信息 1、请完成add函数,在列表后面显示items商品信息。参数items为{name: String, price: Number}组成的数组 2、请完成bind函数,点击每一行的删除按钮(包括通过add增加的行),从列表中删除对应行 3、请注意同步更新统计信息,价格保留小数点后两位 4、列表和统计信息格式请与HTML示例保持一致 5、不要直接手动修改HTML中的代码 6、不要使用第三方库
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
/* 填写样式 */
body,
html {
padding: 0;
margin: 0;
font-size: 14px;
color: #000000;
}
table {
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
thead {
background: #3d444c;
color: #ffffff;
}
td,
th {
border: 1px solid #e1e1e1;
padding: 0;
height: 30px;
line-height: 30px;
text-align: center;
}
</style>
</head>
<body>
<!-- 填写标签 -->
<table id="jsTrolley">
<thead>
<tr>
<th>名称</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>产品1</td>
<td>10.00</td>
<td><a href="javascript:void(0);">删除</a></td>
</tr>
<tr>
<td>产品2</td>
<td>30.20</td>
<td><a href="javascript:void(0);">删除</a></td>
</tr>
<tr>
<td>产品3</td>
<td>20.50</td>
<td><a href="javascript:void(0);">删除</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<th>总计</th>
<td colspan="2">60.70(3件商品)</td>
</tr>
</tfoot>
</table>
<script type="text/javascript">
// 填写JavaScript
function add(items) {
let tbody = document.getElementsByTagName('tbody')[0]
let content = ''
items.forEach(item => {
content += `
<tr>
<td>${item.name}</td>
<td>${item.price.toFixed(2)}</td>
<td><a href="javascript:void(0);">删除</a></td>
</tr>
`
})
tbody.innerHTML += content
this.getAllAccount()
}
function getAllAccount() {
let tbody = document.getElementsByTagName('tbody')[0]
let tfoot = document.getElementsByTagName('tfoot')[0]
const trs = tbody.getElementsByTagName('tr')
const result = Array.from(trs).reduce((prev, curr) => {
return prev + Number(curr.children[1].innerHTML)
}, 0)
tfoot.innerHTML = `
<tr>
<th>总计</th>
<td colspan="2">${result.toFixed(2)}(${trs.length}件商品)</td>
</tr>
`
}
function bind() {
let tbody = document.getElementsByTagName('tbody')[0]
tbody.addEventListener('click', function (e) {
console.log(e.target)
if (e.target.nodeName == 'A') {
e.target.parentElement.parentElement.remove()
getAllAccount()
}
})
}
const items = [
{ name: '商品1', price: 16.25 },
{ name: '商品2', price: 98.25 }
]
add(items)
bind()
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# js80表格排序
系统会在tbody中随机生成一份产品信息表单,如html所示。 请完成 sort 函数,根据参数的要求对表单所有行进行重新排序。 1、type为id、price或者sales,分别对应第1 ~ 3列 2、order为asc或者desc,asc表示升序,desc为降序 3、例如 sort('price', 'asc') 表示按照price列从低到高排序 4、所有表格内容均为数字,每一列数字均不会重复 5、不要使用第三方插件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body,
html {
padding: 0;
margin: 0;
font-size: 14px;
color: #000000;
}
table {
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
thead {
background: #3d444c;
color: #ffffff;
}
td,
th {
border: 1px solid #e1e1e1;
padding: 0;
height: 30px;
line-height: 30px;
text-align: center;
}
</style>
</head>
<body>
<!-- 填写标签 -->
<table>
<thead>
<tr>
<th>id</th>
<th>price</th>
<th>sales</th>
</tr>
</thead>
<tbody id="jsList">
<tr>
<td>1</td>
<td>10.0</td>
<td>800</td>
</tr>
<tr>
<td>2</td>
<td>30.0</td>
<td>600</td>
</tr>
<tr>
<td>3</td>
<td>20.5</td>
<td>700</td>
</tr>
<tr>
<td>4</td>
<td>40.5</td>
<td>500</td>
</tr>
<tr>
<td>5</td>
<td>60.5</td>
<td>300</td>
</tr>
<tr>
<td>6</td>
<td>50.0</td>
<td>400</td>
</tr>
<tr>
<td>7</td>
<td>70.0</td>
<td>200</td>
</tr>
<tr>
<td>8</td>
<td>80.5</td>
<td>100</td>
</tr>
</tbody>
</table>
<script type="text/javascript">
// 填写JavaScript
function sort(type, order) {
const tbody = document.getElementById('jsList')
const trs = document.querySelectorAll('#jsList tr')
let data = []
for(let i = 0; i < trs.length; i++) {
let tds = Array.from(trs[i].childNodes)
data.push({
id: tds[0].innerHTML,
price: tds[1].innerHTML,
sales: tds[2].innerHTML,
tr: trs[i]
})
}
data.sort((a, b) => {
if(order === 'asc') return a[type] - b[type]
else if (order === 'desc') return b[type] - a[type]
else return 0
})
tbody.innerHTML = ''
data.forEach(item => {
tbody.appendChild(item.tr)
})
return data
}
function sort2(type, order) {
// type为id、price或者sales; asc表示升序,desc为降序
// 1.获取父节点
let tbody = document.getElementById('jsList')
// 2.获取数据的行数
let len = tbody.children.length
// 3.遍历获取数组
let domArr = []
let typeNum = type == 'id' ? 0 : type == 'price' ? 1 : type == 'sales' ? 2 : ''
for(let i = 0; i < len; i++) {
domArr.push(tbody.children[i])
}
// 4.重新排列
domArr.sort((a, b) => {
// a, b代表 tr
if(order === 'desc') {
return (b.children[typeNum].innerHTML - a.children[typeNum].innerHTML)
} else if(order === 'asc') {
return (a.children[typeNum].innerHTML - b.children[typeNum].innerHTML)
} else {
return 0
}
})
domArr.forEach(item => {
tbody.appendChild(item)
})
}
sort2('price', 'desc')
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# js81替换链接
页面中存在id=jsContainer的DOM元素。 该DOM元素内会给出一段随机文本,可能包含一些链接,比如https://www.baidu.com,或者 www.baidu.com?from=onlineExam,如果出现链接文本,请给该链接文本加上链接标签,用户点击后能直接在新窗口中打开该链接。 请完成 link 函数,完成该功能 1、container只有纯文本内容,不包含其他dom元素 2、识别所有以http://、https://或者www.开始的链接 3、所有www.开头的链接,默认使用 http 协议 4、所有链接在新窗口打开
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
/* 填写样式 */
a {
color: #00bc9b;
}
</style>
</head>
<body>
<!-- 填写标签 -->
<div id="jsContainer">
这里会给出一段随机文本,可能包含一些链接,比如https://www.baidu.com,或者 www.baidu.com?from=onlineExam,如果出现链接文本,请给该链接文本加上链接标签,用户点击后能直接在新窗口中打开该链接。
</div>
<script type="text/javascript">
// 填写JavaScript
function link() {
const div = document.getElementById('jsContainer')
const reg = /(https?:\/\/)?(www\.\w+(\.(com|cn))*([?]\w+=\w*(&\w+=\w*)*)?(#\w+)?)/g
div.innerHTML = div.innerHTML.replace(reg, function(...arg) {
console.log(arg)
if(arg[1]) {
return `<a target="_blank" href="${arg[1]}${arg[2]}">${arg[0]}</a>`
} else {
return `<a target="_blank" href="http://${arg[2]}">${arg[0]}</a>`
}
})
}
</script>
</body>
</html>
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
# js82倒计时
倒计时是web开发中常见的组件,请完成second和render两个函数,完成倒计时的显示部分 1、second函数的输入为整数,返回{day: Int, hour: Int, min: Int, second: Int} 2、render函数的输入为second函数的输出,将数据在页面对应的DOM元素上显示出来,格式如html所示 3、如果day为0,隐藏对应的DOM元素,否则显示(请直接使用已经实现的css代码) 4、数值不足两位,前面补充0
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
/* 填写样式 */
.hide {
display: none;
}
</style>
</head>
<body>
<!-- 填写标签 -->
<div id="jsCountdown">
<span>01天</span>
<span>02:</span>
<span>03:</span>
<span>04</span>
</div>
<script type="text/javascript">
// 填写JavaScript
function second(second) {
return {
day: parseInt(second / 60 / 60 / 24),
hour: parseInt(second / 60 / 60) % 24,
min: parseInt(second / 60) % 60,
second: second % 60
}
}
function render(data) {
function padding(param) {
if(param < 10) {
return '0' + param
} else {
return param
}
}
const div = document.getElementById('jsCountdown')
div.innerHTML = `
<span class="${data.day == 0 ? 'hide' : ''}">${padding(data.day)}天</span>
<span>${padding(data.hour)}:</span>
<span>${padding(data.min)}:</span>
<span>${padding(data.second)}</span>
`
}
const obj = second(1200000)
render(obj)
function abc() {
var data = second(90061);
console.log(data)
var result = data.day === 1 && data.hour === 1 && data.min === 1 && data.second === 1; return result;
return result;
}
console.log(abc())
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
# js83 双色球机选一注
双色球由33个红球和16个蓝球组成,1注双色球包括6个不重复的红球和1个蓝球。 请阅读给出的页面和代码,完成 randomFn 函数,实现“随机一注”功能,要求如下: 函数返回: 1.以字符串形式输出“随机一注”结果,选中的红蓝球用"|"隔开,红球在前,号码间用半角逗号隔开,如"06,10,13,18,23,27|05" 2.红球和蓝球号码排列顺序 需与页面展示的顺序对应 页面交互: 1.将选中的红球和蓝球(页面中对应DOM元素)用class="active"高亮 2.将选中的球按号码从小到大排列,移至所属组的前方,结果如示意图所示 3.每次执行 randomFn 函数,输出符合要求且不完全重复
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.main .balls {
width: 450px;
padding: 30px 10px 10px;
margin-bottom: 20px;
position: relative;
border-radius: 4px;
}
.main .balls:after {
content: '\20';
clear: both;
display: block;
height: 0;
overflow: hidden;
}
.main .balls span {
position: absolute;
left: 12px;
top: 5px;
font-size: 13px;
}
.main b {
float: left;
width: 30px;
height: 30px;
font-size: 15px;
background: #FFF;
border: 1px solid;
border-radius: 50%;
line-height: 30px;
text-align: center;
margin-right: 8px;
margin-bottom: 8px;
cursor: pointer;
}
.main .red .active {
background: #f56c6c;
color: #FFF;
}
.main .blue .active {
background: #3a8ee6;
color: #FFF;
}
.main .red {
background: #feeff0;
}
.main .red b {
border-color: #f56c6c;
}
.main .blue {
background: #ecf8ff;
}
.main .blue b {
border-color: #3a8ee6;
}
</style>
</head>
<body>
<!-- 填写标签 -->
<div class="main">
<div class="balls red">
<span>红球</span>
<div class="balls-wp">
<b>01</b>
<b>02</b>
<b>03</b>
<b>04</b>
<b>05</b>
<b>06</b>
<b>07</b>
<b>08</b>
<b>09</b>
<b>10</b>
<b>11</b>
<b>12</b>
<b>13</b>
<b>14</b>
<b>15</b>
<b>16</b>
<b>17</b>
<b>18</b>
<b>19</b>
<b>20</b>
<b>21</b>
<b>22</b>
<b>23</b>
<b>24</b>
<b>25</b>
<b>26</b>
<b>27</b>
<b>28</b>
<b>29</b>
<b>30</b>
<b>31</b>
<b>32</b>
<b>33</b>
</div>
</div>
<div class="balls blue">
<span>蓝球</span>
<div class="balls-wp">
<b>01</b>
<b>02</b>
<b>03</b>
<b>04</b>
<b>05</b>
<b>06</b>
<b>07</b>
<b>08</b>
<b>09</b>
<b>10</b>
<b>11</b>
<b>12</b>
<b>13</b>
<b>14</b>
<b>15</b>
<b>16</b>
</div>
</div>
</div>
<script type="text/javascript">
// 填写JavaScript
console.log(randomFn());
function randomFn() {
let redballs = document.querySelectorAll('.red .balls-wp b')
let blueballs = document.querySelectorAll('.blue .balls-wp b')
let reddiv = document.querySelector('.red .balls-wp')
let bluediv = document.querySelector('.blue .balls-wp')
let red = [] // 存储红球
let redDoms = [] // 存储红球对应的dom
// 随机生成6个颜色的红球
while(red.length < 6) {
let num = Math.floor(Math.random() * (redballs.length)) + 1
if(!red.includes(num)) {
red.push(num)
let redDom = redballs[num - 1]
redDom.classList.add('active')
redDoms.push(redDom)
}
}
let blue = Math.floor(Math.random() * (blueballs.length)) + 1
let blueDom = blueballs[blue - 1]
blueDom.classList.add('active')
// 选中的红球,从大到大排列,(插入的时候就是从小到大的顺序)
redDoms = redDoms.sort((a, b) => b.innerHTML - a.innerHTML)
for(let i = 0; i < 6; i++) {
let redballs = document.querySelectorAll('.red .balls-wp b')
// insertBefore是如果原来是子节点则将子节点移动到对应节点前面(删除原本位置)
reddiv.insertBefore(redDoms[i], redballs[0])
}
bluediv.insertBefore(blueDom, blueballs[0])
return redDoms.map(item => item.innerHTML).reverse().join(',') + '|' + blueDom.innerHTML
}
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# js84智能提示
本题展示了一个简化版的搜索框智能提示功能,请按照如下要求完成suggest函数。 1、当输入框的值发生变化时,系统会调用suggest函数,用于显示/隐藏智能提示数据,参数items为一个字符串数组。 2、当items中的字符串和输入框的值匹配时,将匹配的数据依次渲染在ul下的li节点中,并显示.js-suggest节点,否则移除ul下的所有li节点,并隐藏.js-suggest节点 3、输入框的值需要移除两侧空白再进行匹配 4、输入框的值为空时,按照全部不匹配处理 5、字符串使用模糊匹配,比如"北大"能匹配"北大"和"北京大学",但不能匹配"大北京",即按照 /北.?大.?/ 这个正则进行匹配 6、通过在.js-suggest节点上添加/移除 hide 这个class来控制该节点的隐藏/显示 7、当前界面是执行 suggest(['不匹配数据', '根据输入框的值', '从给定字符串数组中筛选出匹配的数据,依次显示在li节点中', '如果没有匹配的数据,请移除所有li节点,并隐藏.js-suggest节点']) 后的结果 8、请不要手动修改html和css 9、不要使用第三方插件 10、请使用ES5语法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.search {
position: relative;
}
.js-input {
width: 450px;
height: 22px;
line-height: 22px;
font-size: 16px;
padding: 8px;
border: 1px solid #cccccc;
outline: none;
}
.js-suggest {
width: 466px;
font-size: 14px;
border: 1px solid #cccccc;
background: #ffffff;
position: absolute;
left: 0;
top: 39px;
}
.js-suggest.hide {
display: none;
}
.js-suggest ul {
display: block;
list-style: none;
padding: 0;
margin: 0;
}
.js-suggest ul li {
color: #000;
font: 14px arial;
line-height: 25px;
padding: 0 8px;
position: relative;
cursor: default;
}
.js-suggest ul li:hover {
background: #f0f0f0;
}
</style>
</head>
<body>
<div class="search">
<div><input type="text" class="js-input" value="垃"></div>
<div class="js-suggest">
<ul>
<li>根据输入框的值</li>
<li>从给定字符串数组中筛选出匹配的数据,依次显示在li节点中</li>
<li>如果没有匹配的数据,请移除所有li节点,并隐藏.js-suggest节点</li>
</ul>
</div>
</div>
<script type="text/javascript">
function suggest(items) {
const input = document.getElementsByClassName('js-input')[0]
const tipDiv = document.getElementsByClassName('js-suggest')[0]
const tipUl = document.querySelectorAll('.js-suggest ul')[0]
let len = tipUl.children.length
// 清空ul里面的内容
for(let i = 0; i < len; i++) {
let li = document.querySelector('li')
tipUl.removeChild(li)
}
let value = input.value.trim() // 输入的内容
if(!value) {
tipDiv.classList.add('hide')
return
}
// 构造reg的时候,ipt如果是特殊字符,要转义
let reg = ''
const regKey = ['(', ')', '.', '?', '^', '/', '\\', '*', '[', ']', '|', '+', '{', '}', '$']
for(i of value) {
if(regKey.includes(i)) {
i = '\\' + i
}
reg += i + '.*?'
}
reg = new RegExp(reg)
for(item of items) {
if(reg.test(item)) {
let tip = document.createElement('li')
tip.innerHTML = item
tipUl.appendChild(tip)
}
}
const len2 = tipUl.children.length
if(len2 == 0) { // 说明没有匹配的数据
tipDiv.classList.add('hide')
} else {
tipDiv.classList.remove('hide')
}
}
// suggest('123254545456我是小垃圾')
function test () {
var ipt = document.querySelector('.js-input');
var dv = document.querySelector('.js-suggest');
var ul = document.querySelector('ul');
ipt.value = '01';
suggest(['01', '2', '3', '4', '5', '6', '7', '8', '9', '0', '10', '110']);
var li = ul.children;
var result = dv.className.indexOf('hide') >= 0;
console.log(result)
// result = result && li.length === 0;
// return result;
}
console.log(test())
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# js86分页
本题展示了一个分页组件,界面中存在id=jsContainer的节点A,系统会随机实例化各种Pagination实例,请按照如下要求补充完成Pagination函数。 1、最多连续显示5页,居中高亮显示current页(如demo1所示) 2、total <= 1 时,隐藏该组件(如demo2所示) 3、如果total<=5,则显示全部页数,隐藏“首页”和“末页”元素(如demo3所示) 4、当current居中不足5页,向后(前)补足5页,隐藏“首页”(“末页”)元素(如demo4和demo5所示) 5、total、current均为正整数,1 <= current <= total 6、当前界面中,节点A为系统执行 new Pagination(节点A,20, 10) 后的展示效果 7、请不要手动修改html和css 8、不要使用第三方插件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.demo{
margin-bottom: 20px;
border: 1px solid #ebedf0;
border-radius: 2px;
padding: 10px;
}
.demo div{
margin-bottom: 10px;
font-size: 14px;
}
.pagination{
box-sizing: border-box;
margin: 0;
padding: 0;
font-size: 14px;
line-height: 1.5;
list-style: none;
display: inline-block;
}
.pagination.hide{
display: none;
}
.pagination li{
position: relative;
display: inline-block;
float: left;
height: 32px;
margin: 0;
padding: 0 15px;
line-height: 30px;
background: #fff;
border: 1px solid #d9d9d9;
border-top-width: 1.02px;
border-left: 0;
cursor: pointer;
transition: color 0.3s, border-color 0.3s;
}
.pagination li:first-child{
border-left: 1px solid #d9d9d9;
border-radius: 4px 0 0 4px;
}
.pagination li:last-child{
border-radius: 0 4px 4px 0;
}
.pagination li:first-child{
box-shadow: none !important;
}
.pagination li.current{
border-color: #1890ff;
color: #1890ff;
border-left: 1px solid #1890ff;
}
.pagination li.current:not(:first-child) {
margin-left: -1px;
}
</style>
</head>
<body>
<div>
<div id="jsContainer">
<ul class="pagination">
<li>首页</li>
<li>8</li>
<li>9</li>
<li class="current">10</li>
<li>11</li>
<li>12</li>
<li>末页</li>
</ul>
</div>
<div class="demo">
<div>(Demo1) total: 10,current: 4</div>
<ul class="pagination">
<li>首页</li>
<li>2</li>
<li>3</li>
<li class="current">4</li>
<li>5</li>
<li>6</li>
<li>末页</li>
</ul>
</div>
<div class="demo">
<div>(Demo2) total: 0,current: 0</div>
<ul class="pagination hide"></ul>
</div>
<div class="demo">
<div>(Demo3) total: 3,current: 2</div>
<ul class="pagination">
<li>1</li>
<li class="current">2</li>
<li>3</li>
</ul>
</div>
<div class="demo">
<div>(Demo4) total: 10,current: 2</div>
<ul class="pagination">
<li>1</li>
<li class="current">2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>末页</li>
</ul>
</div>
<div class="demo">
<div>(Demo5) total: 10,current: 9</div>
<ul class="pagination">
<li>首页</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li class="current">9</li>
<li>10</li>
</ul>
</div>
</div>
<script type="text/javascript">
function Pagination(container, total, current) {
this.total = total;
this.current = current;
this.html = html;
this.val = val;
this.el = createUl(); // 创建分页根组件
if (!this.el) return;
this.el.innerHTML = this.html();
container.appendChild(this.el);
if(this.total <= 1) {
this.el.className = 'hide'; //TODO: 判断是否需要隐藏当前元素
}
function html() {
// total <= 1 时,隐藏该组件
if (this.total <= 1) return '';
let str = '';
// total<=5,则显示全部页数
if(this.total <= 5) {
str += createLiStr(1, this.total, this.current);
return str
} else {
// 当current居中不足5页,向后(前)补足5页,隐藏“首页”(“末页”)元素
let left = 1, right = this.total
if(this.current - 2 <= 1) {
// 这种情况不应该有首页
left = 1
}else {
// 应该有首页
str += '<li>首页</li>'
left = this.current - 2
}
if(this.current + (5 - (this.current - left + 1)) > this.total) {
right = this.total
if(left >= 1 && right - left < 4) {
left = right - 4
}
while(left < 0) {
left ++
}
str += createLiStr(left, right, this.current)
}else {
right = this.current + (5 - (this.current - left + 1))
str += createLiStr(left, right, this.current)
// 这种情况应该有末页
str += '<li>末页</li>'
}
return str
}
//TODO: 生成组件的内部html字符串
return '';
}
function val(current) {
if (arguments.length === 0) return this.current;
if (current < 1 || current > this.total || current === this.current) return;
this.current = current;
this.el.innerHTML = this.html();
};
function createUl() {
const ul = document.createElement('ul')
ul.classList.add('pagination')
return ul
}
function createLiStr(left, right, current) {
let str = ''
for(let i = left; i <= right; i++) {
if( i === current) {
str += `<li class="current">${i}</li>`
} else {
str += `<li>${i}</li>`
}
}
return str
}
}
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# js87移动控制
界面中存在id=jsContainer的节点A,系统会随机生成id为jsLayout的 m行 x n列 表格(m >= 1, n >= 1),并随机选中一个td节点,请按照如下需求实现bind函数 1、bind 函数为document绑定keydown事件,当系统触发上(键值38)下(键值40)左(键值37)右(键值39)按键时,请找到当前选中的td节点,并根据当前指令切换高亮节点,具体效果参考以下图片 2、在第一列往左移动则到达最后一列;在最后一列往右移动则到达第一列;在第一行往上移动则到达最后一行;在最后一行往下移动则到达第一行; 3、请不要手动调用bind函数 4、当前界面为系统在节点A中生成 9 * 9 表格并随机选中一个td节点后的效果 5、请不要手动修改html和css,请不要修改js中的事件绑定方式 6、不要使用第三方插件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
table.game {
font-size: 14px;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
table.game td {
border: 1px solid #e1e1e1;
padding: 0;
height: 30px;
text-align: center;
}
table.game td.current {
background: #1890ff;
}
</style>
</head>
<body>
<div id="jsContainer">
<table class="game">
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="current"></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
function bind() {
const rows = document.querySelectorAll('.game tr')
const cols = rows[0].querySelectorAll('td')
document.onkeydown = event => {
if (!event) return;
var code = event.keyCode || '';
if (!{ '37': 1, '38': 1, '39': 1, '40': 1 }[code]) return;
event.preventDefault && event.preventDefault();
// tr的数量为行
let curr = document.querySelector('.current')
let x = 0, y = 0
Array.from(rows).forEach((tr, index) => {
if(tr.contains(curr)) {
y = index
Array.from(tr.querySelectorAll('td')).forEach((td, i) => {
if(td.classList.contains('current')) {
x = i
}
})
}
})
// 上
if(Number(code) === 38) {
if(y == 0) {
y = rows.length - 1
} else {
y --
}
}
// 下
if(Number(code) === 40) {
if(y == rows.length - 1) {
y = 0
} else {
y ++
}
}
// 左
if(Number(code) === 37) {
if(x == 0) {
x = cols.length - 1
} else {
x --
}
}
// 右
if(Number(code) === 39) {
if(x == cols.length - 1) {
x = 0
} else {
x ++
}
}
curr.classList.remove('current')
rows[y].querySelectorAll('td')[x].classList.add('current')
};
}
bind()
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# JS88 dom节点转成json数据
页面上存在id=jsContainer的节点A,系统会随机在节点A中生成文档片段,请按照如下需求实现 dom2json 函数 1、dom2json需要分析整个节点A的dom结构,并将其结构转换为对应的json对象 2、需要获取dom结构的标签名称(tag),所有属性(attributes),子节点(children) 3、文档片段中的属性形式均为 name="value",解析之后的格式为{name: value}, 属性值为String类型,不需要做解析 4、随机生成的文档片段中,只包含 nodeType 为1(element)和3(text)的节点,不需要考虑其他节点类型 5、纯文本也视为一个节点, json格式为 {tag: 'text', content: '文本内容'},content为文本内容执行trim后的结果,如果该结果为空,则忽略当前节点 6、返回结果中的标签名称不区分大小写 7、如果节点不包含属性值或者子节点,其对应的结果中需要保留attributes以及children字段,例如 {tag: 'div', attributes: {}, children: []} 8、当前界面执行dom2json之后的结果为如下图所示 9、请不要手动修改html和css 10、不要使用第三方插件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
margin: 0;
font-size: 14px;
}
</style>
</head>
<body>
<div id="jsContainer">
<ul class="js-test" id="jsParent">
<li data-index="0">1</li>
<li data-index="1">2</li>
</ul>
<span style="font-weight: bold;">3</span>
4
</div>
<script type="text/javascript">
function dom2json() {
const container = document.getElementById('jsContainer');
return createObj(container);
}
function createObj(element) {
// 创建一个空的对象
const obj = {};
// 获取标签名
obj.tag = element.nodeName.toLowerCase().replace('#', '');
// 如果是文本
if (obj.tag == 'text') {
obj.content = element.data.trim();
}
obj.attributes = {};
if (element.attributes) {
for (let i = 0; i < element.attributes.length; i++) {
const { name, value } = element.attributes[i];
obj.attributes[name] = value;
}
}
// 对孩子进行过滤,去除没有内容的text
const childArr = Array.from(element.childNodes).filter(item => {
return item.nodeName === '#text' && item.data.trim() !== '' || item.nodeName !== '#text';
})
obj.children = [];
if (childArr.length) {
for (let i = 0; i < childArr.length; i++) {
obj.children[i] = createObj(childArr[i]);
}
}
return obj;
}
function abc() {
var el = document.getElementById('jsContainer'); (el || {}).innerHTML = '<em class="a1" data-class="a2"></em>';
var data = dom2json();
var result = isSame(data, {
tag: 'div',
attributes: {
id: 'jsContainer'
},
children: [{
tag: 'em',
attributes: {
class: 'a1',
'data-class': 'a2'
},
children: []
}]
});
console.log(result);
return result;
function isSame(o1, o2) {
var type1 = ({}).toString.call(o1);
var type2 = ({}).toString.call(o2);
if (type1 !== type2) return;
if (type1 === '[object Array]') return o1.join('T_T') === o2.join('T_T');
if (type1 === '[object String]') return o1 === o2;
var key1 = Object.keys(o1).sort((a, b) => a > b ? 1 : a === b ? 0 : -1);
var key2 = Object.keys(o2).sort((a, b) => a > b ? 1 : a === b ? 0 : -1);
if (key1.length !== key2.length) return false;
return key1.every(key => isSame(o1[key], o2[key]));
}
}
abc()
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# js89设置标签
本题展示了一个简化版的标签输入框,功能如下: 1、当用户输入内容并敲回车键时,将输入框的内容在输入框前显示成标签,并清空输入框内容 2、当用户敲删除键时,如果输入框当前没有内容,则删除前一个标签 3、标签需要去掉字符串两端的多余的空格 4、标签不能为空字符串 5、标签不能重复,如果输入已存在的内容相同的标签,则不添加,并清空输入框 6、请补充完成tagInput.init、tagInput.bindEvent、tagInput.addTag、tagInput.removeTag函数,实现上面的需求 7、相关键码值,回车键=13,删除键=8 8、请不要手动修改html和css 9、不要使用第三方插件 10、请使用ES5语法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.tag-input {
position: relative;
border: 1px solid #cccccc;
padding: 0 5px;
display: flex;
flex-flow: row wrap;
}
.js-input {
width: 450px;
height: 22px;
line-height: 22px;
font-size: 16px;
padding: 0;
margin: 5px 0;
outline: none;
border: none;
width: 6.5em;
height: 24px;
line-height: 24px;
}
.tag {
padding: 0 10px;
margin: 5px 5px 5px 0;
background: #25bb9b;
color: #ffffff;
height: 24px;
line-height: 24px;
border-radius: 12px;
font-size: 13px;
}
</style>
</head>
<body>
<div class="tag-input">
<span class="tag">前端</span>
<span class="tag">编程题</span>
<span class="tag">示例</span>
<span class="tag">标签</span>
<input type="text" class="js-input" maxlength="6" placeholder="请输入标签">
</div>
<script type="text/javascript">
var tagInput = {
isInited: false,
init: init,
bindEvent: bindEvent,
addTag: addTag,
removeTag: removeTag
};
tagInput.init();
function init() {
var that = this;
if (that.isInited) return;
that.isInited = true;
// 请修改这一行代码,保存class为js-input的输入框的dom元素引用
that.input = document.querySelector('.js-input');
that.bindEvent();
}
function bindEvent() {
var that = this;
var input = that.input;
if (!input) return;
input.addEventListener('keydown', function (event) {
// 请修改这一行代码,判断用户是否按了回车键
var isEnter = event.keyCode == 13;
// 请修改这一行代码,判断用户是否按了删除键
var isDelete = event.keyCode == 8;
(isEnter || isDelete) && event.preventDefault();
isEnter && that.addTag();
isDelete && that.removeTag();
});
input.parentNode.addEventListener('click', function () {
input.focus();
});
}
function addTag() {
let value = this.input.value.trim()
let parentDom = document.querySelector('.tag-input')
// 获取所有的标签
let tags = document.querySelectorAll('.tag')
let tagContents = Array.from(tags).map(item => {
return item.innerHTML
})
if(value && value != '') {
if(!tagContents.includes(value)) {
const span = document.createElement('span')
span.classList.add('tag')
span.innerHTML = value
parentDom.insertBefore(span, this.input)
// 然后需要把input的内容清空
this.input.value = ''
} else {
// 输出相同的内容,也要清空
this.input.value = ''
}
}
}
function removeTag() {
let value = this.input.value
let parentDom = document.querySelector('.tag-input')
if(value && value.length != 0) {
this.input.value = value.substr(0, value.length - 1)
}else {
let tags = document.querySelectorAll('.tag')
if(tags && tags.length > 0) {
const tagDom = tags[tags.length - 1]
parentDom.removeChild(tagDom)
}
}
}
function abc() {
var input = document.getElementsByClassName('js-input')[0];
var div = input.parentNode;
clear();
input.value = '123';
tagInput.addTag();
input.value = 'abc';
tagInput.addTag();
var oEvent = new window.Event('keydown');
oEvent.keyCode = 8;
input.dispatchEvent(oEvent);
// var right = isSame(['123']);
// return !! right;
function clear() {
var span = div.getElementsByClassName('tag');
for (var i = span.length - 1; i >= 0; i--) {
div.removeChild(span[i]);
}
}
// function isSame(items) {
// var span = div.getElementsByClassName('tag');
// var right = items.length === span.length;
// for (var i = 0,
// l = span.length; i < l; i++) {
// right = right && span[i].innerHTML === items[i];
// }
// return right;
// }
}
console.log(abc())
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# js90选择组件
CheckGroup是一个选择组件类,支持单选和多选 选项参数格式、调用方式以及效果如下
选项参数格式: var options = [{text: '选项a', value: 'a'}, {text: '选项b', value: 'b'}, {text: '选项c', value: 'c'}, {text: '选项d', value: 'd'}];
实例化单选组件: var item = new CheckGroup(document.getElementById('jsCheckGroup'), options); item.val(['a']);
实例化多选组件: var item = new CheckGroup(document.getElementById('jsCheckGroup'), options, true); item.val(['a']);
具体功能和需求如下: 1、单选组件请在 div.checkgroup 元素加上class radius 2、选中时,请在对应选项dom元素加上class selected 3、点击单选选项,如果未选中当前选项则选中当前选项并取消其他选项,否则取消当前选项 4、点击多选选项,如果未选中当前选项则选中当前选项,否则取消当前选项 5、给定的options中, text和value属性的值均为非空字符串 6、val方法的参数和返回值均为数组(单选时数组长度不超过) 7、请阅读代码,并根据注释完成对应代码(方法initHtml、toggleEl、isSelected、val) 8、请不要手动修改html和css 9、不要使用第三方插件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.checkgroup .item {
height: 42px;
line-height: 42px;
padding: 0 10px;
margin: 10px 0;
border: 1px solid #c7c7c7;
border-radius: 6px;
}
.checkgroup.radius .item {
border-radius: 21px;
}
.checkgroup .item.selected {
border: 1px solid #08b292;
background: #08b292;
color: #ffffff;
}
</style>
</head>
<body>
<div id="jsCheckGroup">
<div class="checkgroup radius">
<div data-val="a" class="item selected">选项a</div>
<div data-val="b" class="item">选项b</div>
<div data-val="c" class="item">选项c</div>
<div data-val="d" class="item">选项d</div>
</div>
</div>
<script type="text/javascript">
function CheckGroup(renderTo, options, isMultiple) {
var that = this;
that.renderTo = renderTo;
that.options = options;
that.isMultiple = !!isMultiple;
that.initHtml();
that.initEvent();
}
CheckGroup.prototype.initHtml = fInitHtml;
CheckGroup.prototype.initEvent = fInitEvent;
CheckGroup.prototype.toggleEl = fToggleEl;
CheckGroup.prototype.isSelected = fIsSelected;
CheckGroup.prototype.val = fVal;
function fInitHtml() {
var that = this;
// 请补全代码,拼接html字符串
var sHtml = '<div class="checkgroup radius">';
for(let i = 0; i < that.options.length; i++) {
sHtml += `<div data-val="${that.options[i].value}" class="item">${that.options[i].text}</div>`
}
sHtml += '</div>'
that.renderTo.innerHTML = sHtml;
// 请补全代码,获取checkgroup的dom元素引用
that.el = document.querySelector('.checkgroup');
}
function fInitEvent() {
var that = this;
that.el && that.el.addEventListener('click', function (event) {
var item = event.target;
item.classList.contains('item') && that.toggleEl(item);
});
}
function fToggleEl(item) {
// item为点击的元素
// 根据当前是单选还是多选,以及当前元素是否选中,高亮/取消���亮指定的选项dom元素
var that = this;
if (that.isSelected(item)) {
// 如果点击的元素处于高亮状态,则取消高丽
item.classList.remove('selected')
} else if (that.isMultiple) {
// 多选的情况
item.classList.add('selected')
} else {
const items = document.querySelectorAll('.item')
Array.from(items).forEach(item => {
if(item.classList.contains('selected')) {
item.classList.remove('selected')
}
})
item.classList.add('selected')
}
}
function fIsSelected(item) {
// 请补全代码,判断item是否选中
return item.classList.contains('selected');
}
function fVal(values) {
var that = this;
if (arguments.length === 0) {
// 请补全代码,获取高亮的选项元素
var items = document.querySelectorAll('.selected');
// 请补全代码,获取高亮的选项元素的data-val
var result = Array.from(items).map(item => {
return item.getAttribute('data-val')
});
return result;
}
!that.isMultiple && values.length > 1 && (values.length = 1);
// 请补全代码,获取所有的选项元素
var items = items = that.renderTo.childNodes[0].childNodes;
// 请补全代码,在指定元素上加上高亮的class
Array.from(items).forEach(item => {
if(values.includes(item.getAttribute('data-val'))) {
item.classList.add('selected')
} else {
item.classList.remove('selected')
}
})
}
// var options = [{text: '选项a', value: 'a'}, {text: '选项b', value: 'b'}, {text: '选项c', value: 'c'}, {text: '选项d', value: 'd'}];
// CheckGroup(document.querySelector('.checkgroup'), options, false)
function abc() {
var dv = document.getElementById('jsCheckGroup');
dv.innerHTML = '';
var item = new CheckGroup(dv, [{
text: 'a',
value: '1'
},
{
text: 'b',
value: '2'
}]);
var el = dv.getElementsByClassName('checkgroup')[0];
item.val(['1']);
item.val(['2']);
item.val(['1']);
var result = !!(el.getElementsByClassName('selected').length === 1);
return result;
}
console.log(abc())
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# js/jquery获取data-*属性值
getAttribute()方法
dataset()方法
<li data-id="1" data-abc-id="2">1</li> const li = document.querySelector('li') //data-前缀属性可以在JS中通过dataset取值,更加方便 console.log(li.dataset.id);//1 //data-abc-id连接取值使用驼峰命名法取值 console.log(li.dataset.abcId);//2 //赋值 li.dataset.id = "111";//111 li.dataset.abcId--;//1 //新增data属性 li.dataset.id2 = "100";//100 //删除,设置成null,或者delete li.dataset.id2 = null;//null delete li.dataset.id2;//undefind
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 如何区分/记忆contains/includes/has
如何区分/记忆contains/includes/has (opens new window)
contains
contains作为包含,出现在Element.classList.contains和Node.contains这两个api上,它们都是DOM API,也就是说在操作DOM时遇到包含那就是contains了
includes
includes作为包含出现在Array.prototype.includes和String.prototype.includes上,即js中我们常用的数组和字符串上会用includes
has
has作为判断包含的方法出现在Map/Set/WeakMap/WeakSet/Reflect/FormData上。可以发现与上面的includes 不同,这些has中传入的都是具有唯一性的key参数,即js中如果包含是具有唯一性的包含的话用has,否则是includes
6种JavaScript判断数组是否包含某个值的方法 (opens new window)
利用循环
function contains(arr, val) { for (var i = 0; i < arr.length; i++) { if (arr[i] === val) { return true; } } return false; } contains([1,2,3],3);//true
1
2
3
4
5
6
7
8
9使用数组的some,filter等方法
function contains(arr, val) { return arr.some(item => item === val); } // 使用filter(注意:array.filter(e=>e==x).length > 0等效于array.some(e=>e==x)但some更有效) function contains(arr, val) { return arr.filter((item)=> { return item == val }).length > 0; }
1
2
3
4
5
6
7array.indexOf
[1, 2, 3].indexOf(1);//0 ["foo", "fly63", "baz"].indexOf("fly63");//1 [1, 2, 3].indexOf(4);//-1
1
2
31、indexOf() 方法对大小写敏感!如果要检索的字符串值没有出现,则该方法返回 -1。 2、在比较第一个参数与数组中的每一项时,会使用全等操作符,即要求查找的项必须严格相等 3、数组的位置是ECMAScript5为数组实例新增的,支持的浏览器有IE9+,Firefox,Safari,Opera,Chrome
array.includes
[1, 2, 3].includes(2); // true [1, 2, 3].includes(4); // false // 它还接受可选的第二个参数fromIndex: [1, 2, 3].includes(3, 3); // false [1, 2, 3].includes(3, -1); // true // 不像indexOf,它采用严格相等比较。这意味着您可以检测数组是否包含NaN: [1, 2, NaN].includes(NaN); // true // 也不同于indexOf,includes不会跳过缺失的索引: new Array(5).includes(undefined); // true
1
2
3
4
5
6
7
8
9
10array.find
let numbers = [12, 5, 8, 130, 44]; let result = numbers.find(item => { return item > 8; }); console.log(result);//12 //元素是对象 let items = [ {id: 1, name: 'something'}, {id: 2, name: 'anything'}, {id: 3, name: 'nothing'}, ]; let item = items.find(item => { return item.id == 3; }); console.log(item) //Object { id: 3, name: "nothing" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16利用set中has方法
function contains(arr, val) { return new Set(arr).has(val) } contains([1,2,3],2);//true
1
2
3
4
5
# js91简易计算器
本题展示了一个简化版的计算器,需求如下: 1、除法操作时,如果被除数为0,则结果为0 2、结果如果为小数,最多保留小数点后两位,如 2 / 3 = 0.67(显示0.67), 1 / 2 = 0.5(显示0.5) 3、请阅读并根据提示补充完成函数initEvent、result和calculate 4、请不要手动修改html和css 5、不要使用第三方插件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body,
ul,
li,
select {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul,
li {
list-style: none;
}
.calculator {
max-width: 300px;
margin: 20px auto;
border: 1px solid #eee;
border-radius: 3px;
}
.cal-header {
font-size: 16px;
color: #333;
font-weight: bold;
height: 48px;
line-height: 48px;
border-bottom: 1px solid #eee;
text-align: center;
}
.cal-main {
font-size: 14px;
}
.cal-main .origin-value {
padding: 15px;
height: 40px;
line-height: 40px;
font-size: 20px;
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cal-main .origin-type,
.cal-main .target-type {
padding-left: 5px;
width: 70px;
font-size: 14px;
height: 30px;
border: 1px solid #eee;
background-color: #fff;
vertical-align: middle;
margin-right: 10px;
border-radius: 3px;
}
.cal-keyboard {
overflow: hidden;
}
.cal-items {
overflow: hidden;
}
.cal-items li {
user-select: none;
float: left;
display: inline-block;
width: 75px;
height: 75px;
text-align: center;
line-height: 75px;
border-top: 1px solid #eee;
border-left: 1px solid #eee;
box-sizing: border-box;
}
li:nth-of-type(4n+1) {
border-left: none;
}
li[data-action=operator] {
background: #f5923e;
color: #fff;
}
.buttons {
float: left;
width: 75px;
}
.buttons .btn {
width: 75px;
background-color: #fff;
border-top: 1px solid #eee;
border-left: 1px solid #eee;
height: 150px;
line-height: 150px;
text-align: center;
}
.btn-esc {
color: #ff5a34;
}
.btn-backspace {
position: relative;
}
</style>
</head>
<body>
<div class="calculator">
<header class="cal-header">简易计算器</header>
<main class="cal-main">
<div class="origin-value">0</div>
<div class="cal-keyboard">
<ul class="cal-items">
<li data-action="num">7</li>
<li data-action="num">8</li>
<li data-action="num">9</li>
<li data-action="operator">÷</li>
<li data-action="num">4</li>
<li data-action="num">5</li>
<li data-action="num">6</li>
<li data-action="operator">x</li>
<li data-action="num">1</li>
<li data-action="num">2</li>
<li data-action="num">3</li>
<li data-action="operator">-</li>
<li data-action="num">0</li>
<li data-action="operator">清空</li>
<li data-action="operator">=</li>
<li data-action="operator">+</li>
</ul>
</div>
</main>
</div>
<script type="text/javascript">
var Calculator = {
init: function () {
var that = this;
if (!that.isInited) {
that.isInited = true;
// 保存操作信息
// total: Number, 总的结果
// next: String, 下一个和 total 进行运算的数据
// action: String, 操作符号
that.data = { total: 0, next: '', action: '' };
that.bindEvent();
}
},
bindEvent: function () {
var that = this;
// 请补充代码:获取 .cal-keyboard 元素
var keyboardEl = document.querySelector('.cal-keyboard');
keyboardEl && keyboardEl.addEventListener('click', function (event) {
// 请补充代码:获取当前点击的dom元素
var target = event.target;
// 请补充代码:获取target的 data-action 值
var action = target.dataset.action;
// 请补充代码:获取target的内容
var value = target.innerHTML;
if (action === 'num' || action === 'operator') {
that.result(value, action === 'num');
}
});
},
result: function (action, isNum) {
var that = this;
var data = that.data;
if (isNum) {
data.next = data.next === '0' ? action : (data.next + action);
!data.action && (data.total = 0);
} else if (action === '清空') {
// 请补充代码:设置清空时的对应状态
data.total = 0;
data.next = '';
data.action = '';
} else if (action === '=') {
if (data.next || data.action) {
data.total = that.calculate(data.total, data.next, data.action);
data.next = '';
data.action = '';
}
} else if (!data.next) {
data.action = action;
} else if (data.action) {
data.total = that.calculate(data.total, data.next, data.action);
data.next = '';
data.action = action;
} else {
data.total = +data.next || 0;
data.next = '';
data.action = action;
}
// ���补充代码:获取 .origin-value 元素
var valEl = document.querySelector('.origin-value');
valEl && (valEl.innerHTML = data.next || data.total || '0');
},
calculate: function (n1, n2, operator) {
n1 = +n1 || 0;
n2 = +n2 || 0;
if (operator === '÷') {
// 请补充代码:获取除法的结果
return n2 == 0 ? 0 : Math.floor((n1 / n2) * 100) / 100;
} else if (operator === 'x') {
// 请补充代码:获取乘法的结果
return Math.floor((n1 * n2) * 100) / 100;
} else if (operator === '+') {
// 请补充代码:获取加法的结果
return Math.floor((n1 + n2) * 100) / 100;
} else if (operator === '-') {
// 请补充代码:获取减法的结果
return Math.floor((n1 - n2) * 100) / 100;
}
}
};
Calculator.init();
</script>
</body>
</html>
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# 保留小数位
大家的第一反应可能是使用 toFixed() 方法,但是这个方法在小数位不足的情况下会在后面补 0,比如:
const num = 0.8;
num.toFixed(2) // 0.80
2
可以看到,这个是不符合要求的。
还有一个问题需要注意:小数的相加结果可能并不符合预期,比如:
console.log(0.2 + 0.4) // 0.6000000000000001
这里我们建议使用 Math.floor()
方法来处理小数位,先给结果乘以 100,再通过 Math.floor()
取得整数部分,然后除以 100,得到符合要求的结果,比如:
const num1 = 0.5;
const num2 = 0.68751;
Math.floor(num1 * 100) / 100 // 0.5
Math.floor(num2 * 100) / 100 // 0.68
2
3
4