JS经典题目笔记(this指向,数组去重,时间格式化,字符统计等)

修改this指向

题目:封装函数 f,使 f 的 this 指向指定的对象
示例:无

答案

function bindThis(f, oTarget) {
	if(f.bind){
		return f.bind(oTarget)
	}else{
	    return () => {
		return f.apply(oTarget, arguments)
		}
	}
}

一、主要使用函数
1.Function.prototype.bind()
bind()函数可以改变this的指向,该this指向bind函数的第一个参数,其余的参数作为新函数的参数
语法:function.bind(thisArg,arg1…)(arg11…)
arg里面可以返回一个或者多个参数用做thisArg函数的参数
示例

	a = 10
	const obj = {
		a: 13,
		getA(){
			return this.a
		}
	}
	const b = obj.getA
	console.log(b()) // 10
	console.log(b.bind(obj)) // 13

2.apply和call
apply和call都是改变方法的this指向,区别在于apply第二个参数是一个数组。这两个方法和bind的区别在于bind返回的是一个方法,apply和call返回的是结果,
二、解题思路

  1. 分析此方法就是重写bind方法,用于稍微缜密的思想,首先判断可不可以使用bind方法,如果可以直接就可是得出结果。
  2. 使用apply方法重写bind方法,因为apply是直接返回出的结果,所以重写的时候应该return 一个匿名函数模拟bind方法的返回结果,然后使用arguments返回参数。

获取URL参数

题目
获取 url 中的参数

  1. 指定参数名称,返回该参数的值 或者 空字符串
  2. 不指定参数名称,返回全部的参数对象 或者 {}
  3. 如果存在多个同名参数,则返回数组

示例
输入:http://www.nowcoder.com?key=1&key=2&key=3&test=4#hehe key
输出:[1, 2, 3]

答案

  function getUrlParam(sUrl, sKey) {
    const obj = {} // 创建对象用于保存结果
    if (sUrl.split('?')[1]) { // 判断是否又参数 
      sUrl = sUrl.split('?')[1].split('#')[0].split('&') // 提取出所有参数
      for (let i = 0; i < sUrl.length; i++) { // 遍历含有所有参数的数组
        let temp = sUrl[i].split('=') // 分离参数的名称和值
        if (temp[0] in obj) { // 判断该名称是否已经存在于对象中
          obj[temp[0]] = Array.prototype.concat.apply([], [obj[temp[0]], temp[1]]) // 如果存在了则把此属性的值变为数组保存
        } else {
          obj[temp[0]] = temp[1] // 没有则创建一个新的属性保存
        }
      }
      if (!!sKey) { // 判断是否写入sKey参数 
        return !!obj[sKey] ? obj[sKey] : '' // 查看是否填入的为空白参数
      } else { // 如果没有sKey则直接返回obj对象
        return sKey == '' ? '' : obj
      }
    } else { // 如果没有参数直接返回空对象
      return {}
    }
  }

一、主要使用函数
1.String.prototype.split(separator, limit)
语法:str.split([reg,str],limit)
split()用于切割字符串并返回一个数组,返回的数组不改变原有值
该方法有两个参数

  • separator:该参数可以是正则表达式,也可以是字符,表示从此标记开始切割

  • limit: 该参数表示最多返回几个数组

    示例

	const str = 'hello world'
	str.split('o') // ["hell", " w", "rld"]
	str.split(/o/) // ["hell", " w", "rld"]
	str.split('o', 1) // ["hell"]
	str.split(/o/, 1) // ["hell"]

2.Array.prototype.concat()
concat()方法用于连接两个或两个以上的数组但不改变原有数组
语法:array.concat(arr1,arr2…)
示例

	var a = [1, 2, 3]
	var b = [24, 4, 5]
	var c = [2, 2]
	a.concat(b, c, b) // [1, 2, 3, 24, 4, 5, 2, 2, 24, 4, 5]

二、解题思路

  1. 观察需要选区的片段在哪一个区域,并查找出此区域的相似处
  2. 查找出相似处,使用split方法分割成数组
  3. 该区域是否有值,有值则继续,无值直接返回空对象
  4. 将分割后的的数组判断,值的属性是否相同,利用对象的属性覆盖原则把相同属性的值用concat合并成为一个数组
  5. 最终判断是否传入第二个参数,传入则把此参数相同的对象属性的值返回同时还要判断这第二个参数是不是一个空字符

DOM节点查找

题目
查找两个节点的最近的一个共同父节点,可以包括节点自身

描述
oNode1 和 oNode2 在同一文档中,且不会为相同的节点

   function commonParentNode(oNode1, oNode2) {
    if(oNode1.contains(oNode2)){ 
      return oNode1
    }else {
      return commonParentNode(oNode1.parentElement, oNode2)
    }
  }

一、主要使用函数
1.Node.contains()
contains方法用于判断传入的节点是否为使用节点的后代
语法:node.contains( othreNode )

  • separator:该参数可以是正则表达式,也可以是字符,表示从此标记开始切割

  • limit: 该参数表示最多返回几个数组

    示例

	const a = document.createElement('div')
	a = document.createTextNode('111')
	const b = document.createElement('p')
	b = document.createTextNode('222')
	document.body.appentChild(a)
	a.appentChild(b)
	a.contains(b) // true
	b.contains(a) // false
	document.body.contains(b) // true

二、解题思路

  1. 只要判断两个参数中的其中一个的父级元素包含另一个就可以判断这个父级为共同的父级
  2. 首先判断其中的一个是否包含另一个,包含直接返回自身
  3. 如果不包含则递归此函数并且把其中一个参数变为其元素的父级元素

数组去重

题目
为 Array 对象添加一个去除重复项的方法

示例
输入:[false, true, undefined, null, NaN, 0, 1, {}, {}, ‘a’, ‘a’, NaN]
输出:[false, true, undefined, null, NaN, 0, 1, {}, {}, ‘a’]

  Array.prototype.uniq = function () {
    let arr = [] // 用于保存结果
    let falg = true // 判断NaN的开关
    this.forEach(value => { // 遍历每一个数组
      if (arr.indexOf(value) === -1) { // 判断原数组中的每一个值是否存在于arr
        if (value !== value) { // 过滤NaN
          if (falg) { // 判断是否是第一次保存NaN
            arr.push(value) 
            falg = false
          }
        } else { // 不是NaN 且不重复的保存在arr数组
          arr.push(value)
        }
      }
    })
    return arr
  }

一、主要使用函数
1.Array.prototype.forEach(callback(value, index, arr), thisArg)
语法:array.forEach( (value, index, arr) => {} , thisArg)

  • callback: 回调函数 参数:value(可选):用于返回当前正在处理的元素 index(可选):返回当前元素的下标 ar(可选)r:返回正在操作的数组

  • thisArg(可选):可选参数。当执行回调函数 callback 时,用作 this 的值。

    示例

	const items = ['item1', 'item2', 'item3'];
	const copy = [];
	
	// before
	for (let i=0; i<items.length; i++) {
	  copy.push(items[i]);
	}
	
	// after
	items.forEach((item) => {
	  copy.push(item);
	});

1.Array.prototype.indexOf(searchElement[, fromIndex])

  • 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
	array.indexOf(2, -3); // 0

二、解题思路

  1. 创建一个arr空数组存放无重复的值
  2. 遍历传入的数组,value为当前处理元素,判断arr中是否含有value,没有则push到arr中
  3. 判断特殊值NaN,因为NaN !== NaN 所以indexOf是发现不了NaN的重复,我们也可以利用此特性来发现NaN,当传入第一个NaN后falg = false,不接受后来传入的NaN
  4. 返回arr

时间格式化输出

题目
按所给的时间格式输出指定的时间
格式说明
对于 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 星期五

  function formatDate(t, format) {
    if(!!!format) throw '请写入第二个参数'
    const obj = {
      yyyy: t.getFullYear(),
      yy: ('' + t.getFullYear()).slice(-2),
      MM: ('0' + (t.getMonth() + 1)).slice(-2),
      M: t.getMonth() + 1,
      dd: ('0' + t.getDate()).slice(-2),
      d: t.getDate(),
      HH: ('0' + t.getHours()).slice(-2),
      H: t.getHours(),
      hh: ('0' + (t.getHours() % 12)).slice(-2),
      h: t.getHours() % 12,
      mm: ('0' + t.getMinutes()).slice(-2),
      m: t.getMinutes(),
      ss: ('0' + t.getSeconds()).slice(-2),
      s: t.getSeconds(),
      w: ['日', '一', '二', '三', '四', '五', '六'][t.getDay()]
    }
    return format.replace(/([a-z]+)/ig, $1 => obj[$1])
  }

一、主要使用函数
1.String.prototype.slice()
语法:str.slice(beginIndex[, endIndex])

  • beginIndex: 从该索引出开始截取字符,如果为负数则从结尾往前算是第几位

  • endIndex(可选): 截取到此索引,如果无值,则直接截取到结尾

    示例

	var str1 = 'The morning is upon us.', // str1 的长度 length 是 23。
	    str2 = str1.slice(1, 8),
	    str3 = str1.slice(4, -2),
	    str4 = str1.slice(12),
	    str5 = str1.slice(30);
	console.log(str2); // 输出:he morn
	console.log(str3); // 输出:morning is upon u
	console.log(str4); // 输出:is upon us.
	console.log(str5); // 输出:""

1.String.prototype.replace()
语法:str.replace(regexp|substr, newSubStr|function)

  • regexp (pattern) 一个RegExp 对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。
  • substr (pattern) 一个将被 newSubStr 替换的
    字符串。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。
  • newSubStr (replacement)
    用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名。参考下面的使用字符串作为参数。
  • function (replacement)
    一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考下面的指定一个函数作为参数。

示例

	var str = 'hello world';
	var newstr = str.replace(/hello/i, 'Christmas');
	console.log(newstr);  // Christmas world
	
	var str = '这个人真美';
	var newstr = str.replace('人', '狗');
	console.log(newstr);  // 这个狗真美
	
	var re = /(\w+)\s(\w+)/;
	var str = "John Smith";
	var newstr = str.replace(re, "$2, $1");
	// Smith, John
	console.log(newstr);

二、解题思路

  1. 首先判断是否写了第二个参数,无则报错
  2. 创建一个对象,用于保存格式中用于展示年月日等
  3. 年份的展示,yyyy:直接使用getFullYear,yy:使用‘’空字符转换为字符串类型,然后使用Slice截取后两位
  4. 月份的展示,MM: 使用‘0’字符拼接getMonth方法的值,由于getMonth方法是从0开始记录月份所以需要+1,然后再使用slice截取后两位
  5. 日和小时,使用getDay获取日,使用getHours获取小时,之后同上
  6. 如果是十二小时制的话,需要除以12取余
  7. 分秒,分钟是用getMinutes,秒使用getSecond
  8. 最后使用replace方法,第一个参数使用正则表达式用全局搜索g获取每一个字母串,然后使用obj的属性进行匹配,$1得到的是每匹配到一个字母串的值

颜色字符转换

题目
将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255, 255, 255) 转为 #ffffff

  1. rgb 中每个 , 后面的空格数量不固定
  2. 十六进制表达式使用六位小写字母
  3. 如果输入不符合 rgb 格式,返回原始输入

示例
输入:‘rgb(255, 255, 255)’
输出:#ffffff

答案

   function rgb2hex(sRGB) {
    return sRGB.replace(/^rgb\s*\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)$/i, (a, r, g, b) => {
      return '#' + changeCode(r) + changeCode(g) + changeCode(b)
    })
  }
  function changeCode(num){
    return num < 16 ? '0' + (+num).toString(16) : (+num).toString(16) 
  }

一、主要使用函数
1.Number.prototype.toString()
语法:numObj.toString([radix])

  • radix: 指定要用于数字到字符串的转换的基数(从2到36)。如果未指定 radix 参数,则默认值为 10。

    示例

	var count = 10;
	
	console.log(count.toString());    // 输出 '10'
	console.log((17).toString());     // 输出 '17'
	console.log((17.2).toString());   // 输出 '17.2'
	
	var x = 6;
	
	console.log(x.toString(2));       // 输出 '110'
	console.log((254).toString(16));  // 输出 'fe'
	
	console.log((-10).toString(2));   // 输出 '-1010'
	console.log((-0xff).toString(2)); // 输出 '-11111111'

二、解题思路

  1. 使用正则表达式匹配到每一项的数字组合,利用replace方法的回调函数获取到匹配到的每一个参数。
  2. 创建一个新的方法把回调函数匹配到的数字参数转换成16进制的字符串返回。
  3. 用返回的16进制字符串前面拼接#返回

拓展

  • 加号加一个字符串类型的数字可以转换为数字+'10' // 输出为10

字符串字符统计

题目
统计字符串中每个字符的出现频率,返回一个 Object,key 为统计字符,value 为出现频率

  1. 不限制 key 的顺序
  2. 输入的字符串参数不会为空
  3. 忽略空白字符

示例
输入:‘hello world’
输出:{h: 1, e: 1, l: 3, o: 2, w: 1, r: 1, d: 1}

答案

  function count(str) {
    var obj = {};
    str.replace(/\S/g,function(s){
        !obj[s]?obj[s]=1:obj[s]++;
    })
    return obj;
}

一、主要使用函数
1.String.prototype.replace()

二、解题思路

  1. 全局匹配非空字符,这样可以过滤掉空字符并且可以拿取每一个字符。
  2. 每匹配到一个字符就使用回调函数将此字符作为对象的属性保存,并且在保存时判断是否是第一次保存,如果是第一次保存则给一个初始值,如果不是第一次保存则加一

三、拓展

  • 加号加一个字符串类型的数字可以转换为数字+’10‘ // 输出为10
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值