百度前端学院2015task2自学总结

JavaScript数据类型及语言基础
1.// 判断arr是否为一个数组,返回一个bool值
function isArray(arr) {
    var bool=arr instanceof Array;
    return bool;
}

这是我自己最初写的函数,我刚开始看到这个题目首先想到的是typeof和Instanceof,但是typeof只能区分基本类型:number,string,undefined,boolean,object,function.
instanceof是用来确定一个值是哪种引用类型.所以当时我选择了instanceof,而且我看js高设好像也是用这个方法?
但是最后看到老师的review上面使用的是Object.prototype.toString方法.
这种判断方法更为精确,所以最后isArray()更正为:

function isArray(arr) {
    return Object.prototype.toString.call(arr) === '[object Array]';    
}
2.// 判断fn是否为一个函数,返回一个bool值
function isFunction(fn){
    return typeof(fn)==='function';
}
3.// 使用递归来实现一个深度克隆

可以复制一个目标对象,返回一个完整拷贝,被复制的对象类型会被限制为数字、字符串、布尔、日期、数组、Object对象。不会包含函数、正则对象等
// 测试用例:

var srcObj = {
    a: 1,
    b: {
        b1: ["hello", "hi"],
        b2: "JavaScript"
    }
};
var abObj = srcObj;
var tarObj = cloneObject(srcObj);
srcObj.a = 2;
srcObj.b.b1[0] = "Hello";
console.log(abObj.a);
console.log(abObj.b.b1[0]);
console.log(tarObj.a);      // 1
console.log(tarObj.b.b1[0]);    // "hello"

思路:这里应该要分情况考虑,字符串,数字,布尔应该可以直接复制,因为是基本类型,存放在堆中,所以会创建一个新值。但是引用类型不同,如果直接复制,则传递过来的只是一个指针,并没有达到深度克隆的要求。所以,日期,数组,Object要分别考虑.代码如下:

function cloneObject(src){
    if (!src||typeof src!="object") {
        return src;
    }
    var clone;
    if(src instanceof Date){
        clone=new Date(src.getTime());
        return clone;
    }else if(src instanceof Array){
        clone=[];
        for(var i=0;i<src.length;i++){
            clone[i]=cloneObject(src[i]);
        }
        return clone;
    }else if(src instanceof Object){
        /*
        *第一种方法
        clone={};
        for(var x in src){
            if(src.hasOwnProperty(x)){
                clone[x]=cloneObject(src[x]);
            }
        }
        return clone;*/ 
        /*
        *第二种方法 
        function obj(src){
            function F(){}
            F.prototype=src;
            return new F();
        }
        return obj(src);*/
        //第三种方法
        return Object.create(src);
    }
}

思路:首先判断是否为object,如果不是则直接返回值,对于Date,可以使用getTime()将值取出来,然后再赋给新的Date对象。
对于Array,直接创建一个新数组,然后遍历src,将src的每个值都赋给新数组.
对于object,我这里用了三种方法,第一种是for in判断是否是实例属性,第二种方法是采用原型式继承复制对象,最后一种则是使用ES5的Object.create()方法.

4.//学习数组、字符串、数字等相关方法,

//在util.js中实现以下函数
// 对数组进行去重操作,只考虑数组中元素为数字或字符串,返回一个去重后的数组

//第一种方法:
function uniqArray(arr){
    var result=[];
    for (var i = 0; i < arr.length; i++) {
        if(result.indexOf(arr[i])==-1){
            result.push(arr[i]);
        }
    }
    return result;
}
//第二种方法:
function uniqArray2(arr){
    var result={};
    var ar=[];
    for (var i = 0; i < arr.length; i++) {
        if(!result[arr[i]]){
            ar.push(arr[i]);
            result[arr[i]]=true;
        }
    }
    return ar;
}

思路:第一种方法是我自己写的,主要是利用数组的indexOf()方法循环查找arr数组里的每一项.
第二种方法是看到别人写的,这里采用了键值对的思想,创建一个对象,然后循环arr,检查对象的键,如果不存在,则push.
注意:数组最好不要用for in,因为这里面有坑,以前觉得方便老是用,但是有一次貌似遍历的时候多出来了几项,所以还是老老实实用for循环吧.

5.// 实现一个简单的trim函数,用于去除一个字符串,头部和尾部的空白字符

// 假定空白字符只有半角空格、Tab
// 练习通过循环,以及字符串的一些基本方法,分别扫描字符串str头部和尾部是否
//有连续的空白字符,并且删掉他们,最后返回一个完成去除的字符串

//第一种
function simpleTrim1(str){
    var strr="";
    if(str.charAt(0)==" "){
        for (var i = 0; i < str.length; i++) {
            if(str.charAt(i)!=" "){
                var s=i;
                strr=str.substring(s);
                break;
            }
        }
    }
    if(strr.charAt(strr.length-1)==" "){
        for(var j=strr.length-1;j>=0;j--) {
            if(strr.charAt(j)!=" "){
                var x=j;
                strr=strr.substring(0,x+1);
                break;
            }
        }
    }
    return strr;
}
//第二种:
function simpleTrim2(str){
    var head=0,tail=str.length-1;
    while(str[head]==" "){
        head++;
    }
    while(str[tail]==" "){
        tail--;
    }
    return str.substring(head,tail+1);
}

思路:第一种是利用字符串的charAt方法来判断首尾字符是否为空格,然后用循环标记出不是空格的首位字符以及第一位字符,然后用substring()来剪切字符串.但是我觉得应该没有这么复杂.
第二种就简洁了很多,用head,tail表示首尾,然后用while方法进行循环,求出head和tail的值,最后再一次性用substring方法剪切.

6.// 很多同学肯定对于上面的代码看不下去,接下来,我们真正实现一个trim
// 对字符串头尾进行空格字符的去除、包括全角半角空格、Tab等,返回一个字符串
// 尝试使用一行简洁的正则表达式完成该题目

function trim(str){
    var pattern=/^\s+|\s+$/g;
    //RegExp方法
    if(pattern.test(str)){
        //字符串替换方法
        return str.replace(pattern,"");
    }else{
        return str;
    }
}

思路:这里用到了正则表达式,还有正则的test()和string的replace()方法.
注意:正则里的“|”代表或运算符;

7./ 实现一个遍历数组的方法,针对数组中每一个元素执行fn函数,并将数组索引和元素作为参数传递

function each(arr, fn) {
// your implement
}
// 其中fn函数可以接受两个参数:item和index

// 使用示例
var arr = ['java', 'c', 'php', 'html'];
function output(item) {
    console.log(item)
}
each(arr, output);  // java, c, php, html
// 使用示例
var arr = ['java', 'c', 'php', 'html'];
function output(item, index) {
    console.log(index + ': ' + item)
}
each(arr, output);  // 0:java, 1:c, 2:php, 3:html

代码如下:

function each(arr,fn){
    for(var i=0,len=arr.length;i<len;i++){
        fn(arr[i],i);
    }
}

思路:这题比较简单,就直接用一个循环,里面用一个fn()函数,然后传arr[i],i的值.

8.// 获取一个对象里面第一层元素的数量,返回一个整数
function getObjectLength(obj) {}
// 使用示例
var obj = {
    a: 1,
    b: 2,
    c: {
        c1: 3,
        c2: 4
    }
};
console.log(getObjectLength(obj)); // 3

代码如下:

//第一种:
function getObjectLength(obj){
    var length=0;
    for(var propName in obj){
        if(obj.hasOwnProperty(propName)){
            length++;
        }
     }
     return length;
}
//第二种:
function getObjectLength(obj){
    var length=0;
    return Object.keys(obj).length;//键值
}

思路:for in语句会枚举出对象的所有属性,我们这里不需要全部属性,只需要对象的实例属性,所以还要用hasOwnProperty属性过滤一遍.
第二种:直接用Object.keys(obj)方法,返回的也是实例属性.但是貌似是es5里面的.
另外提一下,Object.getOwnPropertyNames(obj),可以返回obj的所有属性,但是好像也是es5的.

9.// 判断是否为邮箱地址

代码如下:

function isEmail(emailStr){
    var pattern=/^\w+@\w+\.com/;
    var i=emailStr.search(pattern)!==-1;
    return i;
}

思路:我这里写的比较简单,没有考虑那么多.如果要复杂的正则的话我觉得网上有很多例子。

10.//判断是否为手机号
function isMobilePhone(phone){
    var pattern=/^1\d{10}$/;
    var i=phone.search(pattern)!==-1;
    return i;
}

思路:同上,虽然比较简单,但是感觉手机号这样判断应该够了.

DOM
1.// 为element增加一个样式名为newClassName的新样式

代码如下:

function hasClass(element, className) {//首先判断elment有没有这个样式
    var classNames = element.className;
    if (!classNames) {
        return false;
    }
    classNames = classNames.split(/\s+/);
    for (var i = 0, len = classNames.length; i < len; i++) {
        if (classNames[i] === className) {
            return true;
        }
    }
    return false;
}
function addClass(element,newClassName){
    if(!hasClass(element,newClassName)){
        element.className+=" "+newClassName;
    }
}

思路:添加样式之前,首先要判断对象内是不是含有这个样式,采用正则将对象的原classname分隔开,循环判断。如果有,则返回true,没有,返回false.
然后再创建addClass(),调用上面的hasClass()方法,添加新样式.

2.// 移除element中的样式oldClassName

代码如下:

function removeClass(element,oldClassName){
    if(hasClass(element.className)){
        element.className.replace(pattern,"");
    }
}

思路:同上,先调用hasClass()判断,然后再用replace()移除掉oldClassName.

3.// 判断siblingNode和element是否为同一个父元素下的同一级的元素,返回bool值

代码如下:

function isSimbingNode(element,siblingNode){
    return element.parentNode===siblingNode.parentNode;
}

思路:刚开始我还想是不是用nextSibling还是previousSibling来判断,后面发现我有点逗了,直接用parentNode判断就可以了。

4.// 获取element相对于浏览器窗口的位置,返回一个对象{x, y}

代码如下:

function getPosition(element){
    var x=0;
    var y=0;
    var current=element;
    while(current!=null){
        x+=current.offsetLeft;
        y+=current.offsetTop;
        current=current.offsetParent;
    }
    return{
        x:x,
        y:y
    };
}

思路:这个题目刚开始感觉没什么思绪,后面在网上查了一点资料,才有点头绪,首先,来了解一下offsetLeft的概念,offsetLeft是指定对象相对于offsetParent的位置,而offsetParent是指布局中设置position属性的父容器.
通过while循环,offsetLeft累积叠加,直到offsetParent为null,这时,offsetLeft就是相对于版面的距离了,最后的结果就是element相对于浏览器窗口的位置。

接下来挑战一个mini $,它和之前的$是不兼容的,它应该是document.querySelector的功能子集,在不直接使用document.querySelector的情况下,在你的util.js中完成以下任务:

5.// 实现一个简单的Query
function $(selector) {
}
// 可以通过id获取DOM对象,通过#标示,例如
$("#adom"); // 返回id为adom的DOM对象
// 可以通过tagName获取DOM对象,例如
$("a"); // 返回第一个<a>对象
// 可以通过样式名称获取DOM对象,例如
$(".classa"); // 返回第一个样式定义包含classa的对象
// 可以通过attribute匹配获取DOM对象,例如
$("[data-log]"); // 返回第一个包含属性data-log的对象
$("[data-time=2015]"); // 返回第一个包含属性data-time且值为2015的对象
// 可以通过简单的组合提高查询便利性,例如
$("#adom  .classa"); // 返回id为adom的DOM所包含的所有子节点中,第一个样式定义包含classa的对象

代码如下:

function $(selector) {
    var ele = document;
    var sele = selector.replace(/\s+/, ' ').split(' ');    // 去除多余的空格并分割
    for (var i = 0, len = sele.length; i < len; i++) {
        switch (sele[i][0]) {    // 查询第一个字符
            case '#':
                ele = ele.getElementById(sele[i].substring(1));
                break;
            case '.':
                ele = ele.getElementsByClassName(sele[i].substring(1))[0];
                break;
            case '[':
                var valueLoc = sele[i].indexOf('=');//标记'='的位置
                var temp = ele.getElementsByTagName('*');//获取所有元素
                var tLen = temp.length;
                if (valueLoc !== -1) {
                    var key = sele[i].substring(1, valueLoc);
                    var value = sele[i].substring(valueLoc + 1, sele[i].length - 1);
                    for (var j = 0; j < tLen; j++) {
                        if (temp[j].getAttribute(key) === value) {
                            ele = temp[j];
                            break;
                        }
                    }
                }
                else {
                    var key = sele[i].substring(1, sele[i].length - 1);
                    for (var j = 0; j < tLen; j++) {
                        if (temp[j][key]) {
                            ele = temp[j];
                            break;
                        }
                    }
                }
                break;
            default :
                ele = ele.getElementsByTagName(sele[i])[0];
                break;
        }
    }
    if (!ele) {
        ele = null;
    }
    return ele;
}

思路:这道题我感觉有点难度,刚开始我走偏了,用了switch来判断selector的第一个字符,发现有一个部分完成不了,$(“#adom .classa”); // 返回id为adom的DOM所包含的所有子节点中,第一个样式定义包含classa的对象,这个功能我并不能实现,因为只判断第一个元素。后面参考了别人的代码,首先用一个/\s+/配合replace方法将多余的空格替换为一个空格,然后用split方法分割为一个数组。
分割为数组之后,用for循环来一个个判断数组中元素的第一个字符。
‘[‘字符要分情况讨论,在含有’=’的情况下,要将’=’标记,然后取出前面的字符与后面的字符来判断。

4. 事件

我们来继续用封装自己的小jQuery库来实现我们对于JavaScript事件的学习,还是在你的util.js,实现以下函数

1.// 给一个element绑定一个针对event事件的响应,响应函数为listener

代码如下:

function addEvent(element,event,listener){
    if (element.addEventListener) {
        element.addEventListener(event,listener,false);
    }else {
        element.attachEvent("on"+event,listener);
    }
}

思路:为了兼容ie8及以下,所以要用到attachEvent,而且attachEvent只支持事件冒泡,顺便吐槽一下,不知道兼容ie8及以下有什么意义啊,这都什么年月了…

2.// 移除element对象对于event事件发生时执行listener的响应.

代码如下:

function removeEvent(element,event,listener){
    if (element.removeEventListener) {
        element.removeEventListener(event,listener);
    }else{
        element.detachEvent("on"+event,listener);
    }
}

思路:用removEventListener()来移除事件,但是要注意的是,removeEventListener()内传的参数必须和添加事件传的参数一致,并且,如果事件程序是匿名函数的话,不能通过removeEventListener移除.

3.// 实现对click事件的绑定

代码如下:

function addClickEvent(element,listener){
    addEvent(element,'click',listener);
}

思路:调用上面的addEvent为element绑定click事件.

// 实现对于按Enter键时的事件绑定

代码如下:

function addEnterEvent(element, listener) {
    addEvent(element,'keydown',function(event){
        if (event.keyCode==13) {
            listener.call(element,event);
        }
    });
}

思路:键盘事件对象event里有keyCode属性,判断event.keyCode就可以了.
值得注意的是,这里用到了call()方法,call()接收2个参数,第一个参数是this值,第二个参数是你想要传给函数的参数,也可以这么说,call()方法是用传入的对象来替换掉当前对象.所以,在上面的函数中,call()方法用element来替换掉本身的this值.

接下来我们把上面几个函数和$做一下结合,把他们变成$对象的一些方法
addEvent(element, event, listener) -> $.on(element, event, listener);
removeEvent(element, event, listener) -> $.un(element, event, listener);
addClickEvent(element, listener) ->$.click(element, listener);
addEnterEvent(element, listener) -> $.enter(element, listener);

代码如下:

$.on=addEvent;
$.un=removeEvent;
$.click=addClickEvent;
$.enter= addEnterEvent;

接下来考虑这样一个场景,我们需要对一个列表里所有的li增加点击事件的监听
最笨的方法

<ul id="list">
    <li id="item1">Simon</li>
    <li id="item2">Kenner</li>
    <li id="item3">Erik</li>
</ul>
function clickListener(event) {
    console.log(event);
}
$.click($("#item1"), clickListener);
$.click($("#item2"), clickListener);
$.click($("#item3"), clickListener);

上面这段代码要针对每一个item去绑定事件,这样显然是一件很麻烦的事情。
稍微好一些的

<ul id="list">
    <li>Simon</li>
    <li>Kenner</li>
    <li>Erik</li>
</ul>
//我们试图改造一下
function clickListener(event) {
    console.log(event);
}
each($("#list").getElementsByTagName('li'), function(li) {
    addClickEvent(li, clickListener);
});

我们通过自己写的函数,取到id为list这个ul里面的所有li,然后通过遍历给他们绑定事件。这样我们就不需要一个一个去绑定了。但是看看以下代码:

<ul id="list">
    <li id="item1">Simon</li>
    <li id="item2">Kenner</li>
    <li id="item3">Erik</li>
</ul>
<button id="btn">Change</button>
function clickListener(event) {
    console.log(event);
}
function renderList() {
    $("#list").innerHTML = '<li>new item</li>';
}
function init() {
    each($("#list").getElementsByTagName('li'), function(item) {
        $.click(item, clickListener);
    });
    $.click($("#btn"), renderList);
}
init();
事件代理

我们增加了一个按钮,当点击按钮时,改变list里面的项目,这个时候你再点击一下li,绑定事件不再生效了。那是不是我们每次改变了DOM结构或者内容后,都需要重新绑定事件呢?当然不会这么笨,接下来学习一下事件代理,然后实现下面新的方法:
代码如下:

function delegateEvent(element,tag,eventName,listener){
    addEvent(element,eventName, function(event){
        if (event.target&&event.target.nodeName.toLowerCase()==tag) {
            listener.call(event.target,event);
        }
    });
}
$.delegate=delegateEvent;

思路:事件代理以前没接触过,翻阅一些资料后,发现这真是个好东西啊,因为以前写过一些Demo,当创建新的节点时,我必须将事件函数写入到创建节点的函数中来绑定,有了事件代理之后,我可以直接将事件绑定到父元素上面,事件代理的原理是利用了事件的冒泡机制,因为冒泡事件是从具体的元素向上传递直到window,所以我们可以将事件绑定到想要添加的元素的父元素上,在父元素上面来捕获事件,通过判断event.target.nodeName或者event.target.tagName来捕获事件,这里要提一下nodeNametagName的区别,nodeName会显示所有节点的名称,而tagName只会显示元素节点的名称,但是他们都是输出的大写字母,例如:DIV.所以用toLowerCase()将他们转化.另外,这里也需要call()方法变更函数的作用域.

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值