Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JavaScript] 编程题 #17

Open
zgfang1993 opened this issue Mar 14, 2017 · 0 comments
Open

[JavaScript] 编程题 #17

zgfang1993 opened this issue Mar 14, 2017 · 0 comments

Comments

@zgfang1993
Copy link
Owner

zgfang1993 commented Mar 14, 2017

1.实现一个Lazyman

要求:

LazyMan("fangfang"); // "Hi! This is fangfang!"

//延迟任务
LazyMan("fangfang").sleep(10).eat("dinner"); 
/*
 Hi! This is fangfang!
 Wake up after 10s!
 Eat dinner~
 */

//链式调用
LazyMan("fangfang").eat("dinner").eat("supper");
/*
Hi! This is fangfang!
Eat dinner~
Eat supper~
*/

//更改任务顺序
LazyMan("fangfang").sleepFirst(5).eat("supper");
/*
Wake up after 5s!
Hi! This is fangfang!
Eat supper~
*/

这是典型的JavaScript流程控制,问题的关键是如何实现任务的顺序执行。在Express有一个类似的东西叫中间件,每一个中间件执行完成后会调用next()函数,这个函数用来调用下一个中间件。

知识点

  • 方法链式调用
  • 类的使用和面向对象编程的思路

题解:

  • 首先创建一个任务队列
  • 把所有的任务都挂到原型上,依次push/unshift到队列里
  • 每个任务返回this,实现链式调用
  • 然后利用next()函数来控制任务的顺序执行

不清楚的debug看一下顺序,就能理解。

function _LazyMan(name) {
    this.tasks = [];  //创建一个任务队列
    var self = this;
    var fn =(function(n){
        var name = n;
        return function(){
            console.log("Hi! This is " + name + "!");
            self.next();
        }
    })(name);
    this.tasks.push(fn); //把第一个任务存放到任务队列

    setTimeout(function(){
        self.next();  // 在下一个事件循环启动任务
    }, 0);
}


/*
 *事件调度函数
 */
//next() 控制任务执行顺序
_LazyMan.prototype.next = function() {
    var fn = this.tasks.shift(); //获取第一个任务,并从任务列表删除
    fn && fn();
}
// eat sleep 把任务放到任务队列,返回this
_LazyMan.prototype.eat = function(name) {
    var self = this;
    var fn =(function(name){
        return function(){
            console.log("Eat " + name + "~");
            self.next()
        }
    })(name);
    this.tasks.push(fn);
    return this; // 实现链式调用
}
_LazyMan.prototype.sleep = function(time) {
    var self = this;
    var fn = (function(time){
        return function() {
            setTimeout(function(){
                console.log("Wake up after " + time + "s!");
                self.next();
            }, time * 1000);
        }
    })(time);
    this.tasks.push(fn); //把任务函数存放到任务队列
    return this;
}
_LazyMan.prototype.sleepFirst = function(time) {
    var self = this;
    var fn = (function(time) {
        return function() {
            setTimeout(function() {
                console.log("Wake up after " + time + "s!");
                self.next();
            }, time * 1000);
        }
    })(time);
    this.tasks.unshift(fn); //添加到任务列表开头
    return this;
}

/* 封装 */
function LazyMan(name){
    return new _LazyMan(name);
}

2. 改变this指向

要求:

bindThis(function(a, b){return this.test + a + b}, {test: 1})(2, 3);   //6

题解:

function bindThis(f, oTarget) {
    return function(){
        return  f.apply(oTarget,arguments);
    }
}

3.获取 url 中的参数

要求:

  1. 指定参数名称,返回该参数的值 或者 空字符串
  2. 不指定参数名称,返回全部的参数对象 或者 {}
  3. 如果存在多个同名参数,则返回数组
getUrlParam('http://www.nowcoder.com?key=1&key=2&key=3&test=4#hehe', 'key')
//[1,2,3]

getUrlParam('http://www.nowcoder.com?key1=1&key2=2&key3=3&key3=3&test=4#hehe');
//{key1: "1", key2: "2", key3: Array[2], test: "4"}

getUrlParam('http://www.nowcoder.com?key1=1&key2=2&key3=3&key3=5&test=4#hehe','key3');
//["3", "5"]

题解:

function getUrlParam(sUrl,sKey){
    var result = {};
    sUrl.replace(/\??(\w+)=(\w+)&?/g,function(a,k,v){ //匹配值 第一个值 第二个值
        if(result[k] !== void 0){
            var t = result[k];
            result[k] = [].concat(t,v); //相同的key放一起
        }else{
            result[k] = v;
        }
    });
    
    if(sKey === void 0){    //sKey undefined 返回对象
        return result;
    }else{          // 返回数组
        return result[sKey] || '';
    }
}

4.查找共同父节点

要求:

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

commonParentNode(oNode1, oNode2); //返回最近的一个共同父节点

题解:

//递归的方法
function commonParentNode(oNode1, oNode2) {
    if(oNode1.contains(oNode2)){
        return oNode1;
    }else{
        return commonParentNode(oNode1.parentNode,oNode2);
    }
}

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

要求:

[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN].uniq();
//[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a']

题解:

Array.prototype.uniq = function () {
   var resArr = [];
   var flag = true;
     
   for(var i=0;i<this.length;i++){
       if(resArr.indexOf(this[i]) == -1){
           if(this[i] != this[i]){   //排除 NaN
              if(flag){
                   resArr.push(this[i]);
                   flag = false;
              }
           }else{
                resArr.push(this[i]);
           }
       }
   }
    return resArr;
}

6.斐波那契数列

要求:

用 JavaScript 实现斐波那契数列函数,返回第n个斐波那契数。 f(1) = 1, f(2) = 1 等
斐波那契数列:1、1、2、3、5、8…… (后一位是前两位之和)

fibonacci(6); //8

题解:

//递归的方法
function fibonacci(n){
    if(n==1||n==2)
        return 1;
    return fibonacci(n-1)+fibonacci(n-2);
}

7.统计字符串中每个字符的出现频率

输出最多的那个字符及出现次数 看14题

要求:

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

  1. 不限制 key 的顺序
  2. 输入的字符串参数不会为空
  3. 忽略空白字符
count('hello world');
//{h: 1, e: 1, l: 3, o: 2, w: 1, r: 1, d: 1}

题解:

function count(str) {
	var obj = {};
    str = str.replace(/\s/g,'');
    var strArr = str.split("");
    strArr.forEach(function(ele){
        if(obj[ele]){
        	obj[ele]++;
    	}else{
            obj[ele] = 1;
        }
    });
    return obj;
}

8.转换为驼峰格式

要求:

css 中经常有类似 background-image 这种通过 - 连接的字符,通过 javascript 设置样式的时候需要将这种样式转换成 backgroundImage 驼峰格式,请完成此转换功能

  1. 以 - 为分隔符,将第二个起的非空单词首字母转为大写
  2. -webkit-border-image 转换后的结果为 webkitBorderImage
cssStyle2DomStyle('font-size');  // "fontSize"
cssStyle2DomStyle('-webkit-border-image'); //"webkitBorderImage"

知识点:

  • 正则
  • split/join/replace/map/filter

题解:

//方法一:想复杂了,方法二更简单
function cssStyle2DomStyle(sName) {
    function isNotEmpty(value) { //除去空元素
        return value != "";
    }
    sName = sName.split(/\-/);     //["", "webkit", "border", "image"]
    sName = sName.filter(isNotEmpty);  //["webkit", "border", "image"]

    sName = sName.map(function (ele,index,arr) {
        if(index>0 ){ //从第二个元素开始首字母大写
          return  ele.replace(/\b(\w)/,function(f){
                return f.toUpperCase()
            });
        }else{
            return ele;
        }
    }).join(""); //转换为字符串
    return sName;
}
/*
方法二: 正则
/(?!^)\-(\w)(\w+)/g.exec("-webkit-border-image");  //(?!^) 不是第一个  ["-border", "b", "order"]
*/
function cssStyle2DomStyle(sName) {  //'-webkit-border-image'
  return sName.replace(/(?!^)\-(\w)(\w+)/g, function (a, b, c) {
        return b.toUpperCase() + c.toLowerCase(); //"-webkitBorderImage"
    }).replace(/^\-/, '');  //"webkitBorderImage"
}
//方法三: 正则
function cssStyle2DomStyle(sName) {
    return sName.replace(/\-[a-z]/g, function (a, b) {
        return b == 0 ? a.replace('-', '') : a.replace('-', '').toUpperCase();
    });
}

9.优雅的方式求出数组的前10个元素之和

要求:
有一个长度为100的数组,请以优雅的方式求出该数组的前10个元素之和

知识点:

  • reduce
  • slice

题解:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];//假装我有100个
var sum = 0;
 
sum = arr.slice(0, 10).reduce(function(pre, current) {
  return pre + current;
});
 
console.log(sum); //55

10.创建数组 easy

要求:
创建一个1-100的数组,按顺序递增

题解:

var arr=[];
for(var i=0;i<100;i++){
arr.push(i+1);
}

11.输出数字之和

要求:
字符串(包含中文、数字)要求:写一个函数,把这些数字加起来,输出这些数字的和。
E.g “我30你40他50” →120
题解:

function sum(str){
    str = str.replace(/[\u4e00-\u9fa5]+/g," ");
    str = str.trim().split(" ");
    var sum = str.reduce(function (pre,curr) {
        return parseInt(pre)+parseInt(curr);
    })
    return sum;

}

12.不使用中间变量交换两个数的值

要求:
不使用中间变量交换两个数的值
题解:
第一种:加减法

a = a + b;
b = a - b;
a = a - b;

该方法可以交换整型和浮点型数值的变量,但在处理浮点型的时候有可能出现精度的损失,
例如对数据:
a = 3.123456
b = 1234567.000000
交换后各变量值变为:
a = 1234567.000000
b = 3.125000
很明显,原来a的值在交换给b的过程中发生了精度损失。

第二种:乘除法

a = a * b; 
b = a / b;
a = a / b;

乘除法更像是加减法向乘除运算的映射,它与加减法类似:可以处理整型和浮点型变量,但在处理浮点型变量时也存在精度损失问题。而且乘除法比加减法要多一条约束:b必不为0。

可能经验上的某种直觉告诉我们:加减法和乘除法可能会溢出,而且乘除的溢出会特别严重。其实不然,采用这两种方法都不会溢出。以加减法为例,第一步的加运算可能会造成溢出,但它所造成的溢出会在后边的减运算中被溢出回来。

第三种:异或法

a ^= b;    //a=a^b
b ^= a;    //b=b^(a^b)=b^a^b=b^b^a=0^a=a
a ^= b;    //a=(a^b)^a=a^b^a=a^a^b=0^b=b

异或法可以完成对整型变量的交换,对于浮点型变量它无法完成交换。

13.输出什么

要求:

function a(x,y,z){
    x= 4;
    y=5;
    z=6;
    console.log(arguments[2]);
};
a(1,2,3);  //6

题解:
The arguments object is an Array-like object corresponding to the arguments passed to a function.
arguments是一个 object, z 就是 arguments[2], 所以对于 z的修改就是对 arguments[2] 的修改.

14.判断一个字符串中出现次数最多的字符,统计这个次数

//方法一:正则
function findMaxTimes(str) {
    var tmp = str.split('').sort().join(''); //" dehllloorw"
    var key = '';
    var times = 0;
    var re = /(\w)\1+/g;    //匹配连续相同的字符

    tmp.replace(re, function($0, $1) {
        if (times < $0.length) {
            times = $0.length; 
            key = $1;
        }
    });

    return {
        key: key,
        times: times
    }
}
//方法二:两次循环
maxChar('hello world');
function maxChar(str) {
    var obj = {};
    var arr = [];
    var letter;
    //先放到一个对象里
    for(var i = 0,len = str.length;i<len;i++){
        letter = str[i];
        if(!obj[letter]){
            obj[letter] = 1;
        }else{
            obj[letter]++;
        }
    }
   //循环对象属性
    var max_key,max_times=0;
    for(key in obj){
        if(max_times<obj[key]){
            max_times= obj[key];
            max_key = key;
        }
    }
    return {
        key: max_key,
        times: max_times
   }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant