Javascript学习路程(更新中)

一、Array数组的函数

var arr = new Array(1,2,3);
var arr = [1,2,3];
  1. push() // pop()
  2. unshift() // shift()
  3. sort()
  4. reverse()
  5. concat()
  6. splice() 与 slice() 注意两者区别
  7. join()

二、arguments不定参

function css(obj,name,value){
    if(arguments.length == 2){
        return obj.style[name];
    }
    if(arguments.length == 3){
        obj.style[name] = value;
    }
}

window.onload = function(){
    var div = document.getElementById('box');
    css(div,'background','green');
    alert(css(div,'width'));
}

注:
1. 此处arguments.length代表实参的个数
2. argument是一个伪数组对象(伪数组对象不能调用数组的方法,但是类似数组有length等属性)。可以使用Array.prototoype.slice.call(arguments,0)将其转换为数组,从而可以调用数组的方法。

补充:现在已经不推荐使用arguments.callee,原因是访问arguments是个很昂贵的操作,因为arguments本身是一个”很大”的对象,每次递归调用时都要重新创建,影响现代浏览器的性能,还会影响闭包。

三、样式优先级

<div id="box"></div>
<input type="button" id="btn1" value="变红" onclick="toRed();">
<input type="button" id="btn2" value="变绿" onclick="toGreen();">
#box{
    width: 100px;
    height: 100px;
    background: pink;
}
function toRed(){
    var box = document.getElementById('box');
    box.style.background= "red";
}
function toGreen(){
    var box = document.getElementById('box');
    box.className = "green";
}

PS:这里可以通过添加class从而将颜色变绿,再通过添加style行间样式从而将颜色变红。但是不可以先将颜色变红,在变绿,因为此处style样式优先级高于class。

* < 标签 < class < id < style(行间样式)

四、获取非行间样式

  1. 之前我们使用style属性,仅仅只能获取和设置行内的样式。
  2. 如果是通过内联<style>或链接<link>提供的样式规则就无可奈何了,然后我们又学习了getComputedStyle 和currentStyle,这样可以获取非行间样式,但是无法设置非行间样式。
<div id="box"></div>
#box{
    width: 100px;
    height: 100px;
    background-color: red;
}
//获取非行间样式
function getStyle(obj,name){
    if(obj.currentStyle){
        //IE兼容
        return obj.currentStyle[name];
    }
    else{
        //FF兼容
        return getComputedStyle(obj,null)[name];
    }
}

PS:获取非行间样式会出现兼容性问题


五、基本包装类型

基本包装类型是什么?
答:string、boolean、number是一种特殊的引用类型,因为可以调用一些系统内置的属性和方法,但是不能添加方法和属性(prototype除外)

var sub = 'hello';
sub.substring(2);   //处于"读取模式"

2017/6/24补充:每当读取一个基本类型(null和undefined除外)的时候,后台会自动为我们创建一个相对应的基本包装类型,便于调用方法的操作。

以上代码第二行访问sub的时候,访问过程处于”读取模式”。
而在读取模式中访问字符串时,后台都会自动完成以下处理
1. 创建相对应类型的对象
2. 执行指定的方法
3. 销毁该对象

也就是说,以下两段代码是等价的

sub.substring(2);
var sub = new String('hello');
sub.substring(2);
sub = null;

不仅是string类型、对于boolean和number类型也同样适用。

那么引用类型和基本包装类型有什么区别呢?

区别就是对象的生命周期不同。

引用类型会在执行流离开作用域之前都一直保存在内存中;
基本包装类型只存在一行代码的瞬间,执行则销毁。

不信看下面的代码

var sub = 'hello';
sub.color = "red"; //读取模式
alert(sub.color); //undefined 另外一个读取模式
  1. string.split()
  2. string.search() // string.indexOf() // string.match()
  3. string.toLowerCase() // string.toUppercase()
  4. string.slice() // string.substring() // string.substr()
  5. string.charAt()
  6. string.concat()
  7. string.replace()

PS:new sub = new String('hello'); //sub是object类型,不推荐

  1. number.toString()
  2. number.toLocaleString()
  3. number.toFixed()


六、深入javascript原型和原型链

推荐链接:
1.http://blog.csdn.net/ljl157011/article/details/19677059
2.http://www.cnblogs.com/alichengyin/p/4852616.html
3.http://bbs.csdn.net/topics/390520864
补充链接:
强烈推荐:http://www.jianshu.com/p/f2e2323ee756

七、无缝滑动

<a href="javascript:void(0)">向左走</a>
<a href="javascript:void(0)">向右走</a>
<div id="div1">
    <ul>
        <li><img src="./img/1.jpg"></li>
        <li><img src="./img/2.jpg"></li>
        <li><img src="./img/3.jpg"></li>
        <li><img src="./img/4.jpg"></li>
    </ul>
</div>
        *{
            margin: 0;
            padding: 0;
        }
        #div1{
            width:712px;
            height: 108px;
            margin: 200px auto;
            background-color: red;
            position: relative;  //*
            overflow: hidden;  //*
        }
        #div1 ul{
            width: 712px;
            height: 108px;
            list-style: none;
            position: absolute;  //*
            top: 0;
            left: 0;            
        }
        #div1 ul li img{
            display: block;
            width: 178px;
            height: 108px;
            float: left;
        }
window.onload = function(){
    var div1 = document.getElementById('div1');
    var ul = div1.getElementsByTagName('ul')[0];
    var img = ul.getElementsByTagName('img');
    var timer = null;
    var speed = -3;

    function interval(){
        timer = setInterval(function(){

            if(ul.offsetLeft < -(ul.offsetWidth/2))
            {
                ul.style.left = 0;
            }

            if(ul.offsetLeft > 0){
                ul.style.left = -ul.offsetWidth/2 + 'px';
            }

            ul.style.left = ul.offsetLeft + speed + 'px';
        },30);
    }


    ul.innerHTML += ul.innerHTML;
    ul.style.width = img[0].offsetWidth * img.length + 'px';

    interval();

    div1.onmouseover = function(){
        clearInterval(timer);
    };

    div1.onmouseout = function(){
        interval();
    };

    var a = document.getElementsByTagName('a');
    a[0].onclick = function(){
        speed = -3;
    };
    a[1].onclick = function(){
        speed = 3;
    };
};

PS:掌握无缝滑动的原理
1. div、ul与li三者width的关系
2. 元素节点.offsetWidth 与 offsetLeft
3. 出现的bug

    ul.style.width= '170'   //错误
    ul.style.width = 170 + 'px';  //正确


八、createTextNode与innerHTML区别

<div id="box"></div>
var son_box = document.getElementById('box');

//错误的写法如下:
//var son_text = son_div.createTextNode('box_son!!');

//正确的写法如下:
var son_text = document.createTextNode('box_son');
son_div.appendChild(son_text);

box.appendChild(son_div);

PS: 经过实验之后,突然冒出一个想法,createTextNode和innerHTML有什么区别,明明实现的效果一样。

区别:
createTextNode:属于XML Dom,只是纯粹创造了文本节点,所以返回的效果也就是纯文本内容,HTML代码不会被解析。

innerHTML:属于HTML Dom,会将文本中包含的HTML代码实现效果

九、Element类型的nodeName属性

<div id="box"></div>
var box = document.getElementById('box');
if(box.nodeName.toLowerCase() == "div"){
  `//box.nodeName ---> "DIV"
   //此处也可用tagName代替nodeName 
// box.nodeName.toLowerCase() ---> "div"
    alert('true');
}else{
    alert('false');
}

PS:Element.nodeName实际上输出的是“DIV”,而非“div”,因此进行判断的时候需要toLowerCase()将其转换为小写字母。
注:在HTML中标签名始终都以全部大写表示。

十、document.write()

很少用document.write()将内容输出至页面,今天看书的时候发现如果在页面加载过程中使用和在页面加载完dom节点后使用是不同的结果。让爸爸这就用代码来实现一下…

<span>111</span>

页面加载过程中使用:

document.write('<span>222</span>'); //此时页面显示222 111

页面加载之后使用:

window.onload = function(){
    document.write('<span>222</span>'); //此时页面只显示222
};

PS:前面的例子中使用document.write()在页面被呈现的过程中直接向其中输出了内容
如果在文档加载结束后调用document.write(),那么输出的内容将会重写整个页面

十一、HTML Collection 动态性

HTMLCollection这个集合是“动态性”的,因此只要有新的元素被添加到页面中,这个元素也会被添加到该集合中。浏览器不会将创建的所有集合都保存在一个列表中,而是在下一次访问集合时再更新集合。

<div>hello 1</div>
<div>hello 2</div>

错误写法:

var divs = document.getElementsByTagName('div');
for(var i=0; i<divs.length; i++){   
   `//divs.length的值在每次循环后都会递增
    var div = document.createElement("div");
    document.body.appendChild(div);
}

正确写法:

var divs = document.getElementsByTagName('div');
var divs_length = divs.length;
for(var i=0; i<divs_length; i++){   
   `//将divs.length初始值赋给变量
    var div = document.createElement("div");
    document.body.appendChild(div);
}


十二、childNodes兼容性

<ul id="list">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
window.onload = function(){
    var ul = document.getElementById('ul');
    var lis = ul.childNodes;
    alert(lis.length); //返回3 或 7
}

PS:childNodes在IE与其它的浏览器返回结果不一致,因为在非IE中,标准的DOM 具有识别空白文本节点的功能,所以在火狐浏览器是7个,而IE自动忽略了,如果要保持一致的子元素节点,需要手工忽略掉它。
推荐:使用children属性
解决方法:

window.onload = function(){
    var ul = document.getElementById('list');
    for(var i=0; i<ul.childNodes; i++){
        //如果是元素节点
        if(ul.childNodes[i].nodeType == 1){
            alert('元素节点:'+ ul.childNodes[i].nodeName);
        }
        //如果是文本节点
        if(ul.childNodes[i].nodeType == 3){
            alert('文本节点:'+ ul.childNodes[i].nodeValue);
        }
    }
};


十三、innerText与textContent兼容性

//获取文本内容(如有html直接过滤掉)
document.getElmentById('box').innerText; 
//设置文本内容(如果有html不会解析)
document.getElementById('box').innerText = '<strong>hello</strong'>; 

PS:除了Firefox之外,其他浏览器均支持这个方法。但Firefox 的DOM3 级提供了另外一个类似的属性:textContent,做上兼容即可通用。

解决方法:

function getInnerText(element) {
    return (typeof element.textContent == 'string') ?element.textContent : element.innerText;
}

function setInnerText(element, text) {
    if (typeof element.textContent == 'string') {
    element.textContent = text;
    } else {
        element.innerText = text;
    }
}

补充:innerHTML与innerText区别

document.getElementById("box").innerText = "<strong>hello</strong>";

结果如图:
这里写图片描述

document.getElementById("box").innerHTML = "<strong>hello</strong>";

结果如图:
这里写图片描述

<div id="box"><strong>hello</strong></div>
alert(document.getElementById("box").innerText);//<strong>hello</strong>
alert(document.getElementById("box").innerHTML);//hello


十四、添加与移除Class

添加Class:

<div id="box">box</div>
<div class="aaa bob"></div>
var box = document.getElementById('box');
addClass(obj,'aaa');
var div = document.getElementsByTagName('div')[1];
removeClass(obj,'bbb');

//是否存在该class
function existClass(obj,className){
    if(obj.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))){
        return true;
    }
    return false;
}

//添加Class
function addClass(obj,className){
    //如果className不存在
    if(!existClass(obj,className)){
        obj.className += ' ' + className;
    }
}

//移除Class
function removeClass(obj,className){
    //如果className存在
    if(existClass(obj,className)){
        obj.className = obj.className.replace(new RegExp('(\\s|^)' + className + '(\\s|$)'),' ');
    }
}


十五、获取可视区窗口大小 兼容性

    if(typeof window.innerWidth != 'undefined'){
        //FF
        return {
            width : window.innerWidth,
            height : window.innerHeight
        }
    }else{
        //IE、safari、chrome
        return{
            width : document.documentElement.clientWidth,
            height : document.documentElement.clientHeight
        }
    }

PS:document.documentElementclientWidth.clientWidth只是当前页面可视区大小,当页面中有滚动条时,还需要另外添加scrollTop的长度。

为什么不用document.body.clientWidth?
答:因为如果不设置body的长宽,那么body只能靠包含的子元素撑起大小,就不符合当前可视区的大小。

十六、闭包 BUG

闭包:指有权访问另一个函数作用域中的变量的函数
详细了解还需要自行百度:闭包、作用域链、各个执行环境、活动对象、变量对象的联系等。

强烈推荐:
http://blog.leapoahead.com/2015/09/15/js-closure/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

BUG:作用域链的配置机制引出了一个bug,即闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。比如:

function fn(){
    var result = new Array();

    for(var i=0;i<10;i++){
        result[i] = function(){
            return i;
        };
    }

    return result;
}

PS: 这个函数会返回一个函数数组。
从表面上来看,似乎每个函数都应该返回自己的索引值。即位置0的函数返回0,位置1的函数返回1,以此类推。
但实际上,每个函数都返回10.因为每个函数的作用域链中都保存着外边函数fn的活动对象,所以它们引用的都是同一个变量i。当fn()函数返回后,变量i的值为10,此时每个函数都引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都是10。


解决方法:

function fn(){
    var result = new Array();

    for(var i=0;i<10;i++){
        result[i] = function(num){
            return function(){
                return num; 
            }
        }(i);
    }
    return result;
}

PS: 此时每个很熟都会返回各自不同的索引值了。
没有把闭包直接赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时,我们传入了变量i。由于函数参数是按值传递的,所以就会将变量i的当前值赋值给参数num。而在这个匿名函数内部,又创建并返回了一个访问num的闭包。这样一来,result数组中的每个函数都有自己num变量的一个副本,因此就可以返回各自不同的数值了。

十七、offsetParent与offsetTop

<body>
<div id="parent">
    <div id="son"></div>
</div>
</body>
<style type="text/css">
    body{
        margin: 0px;
        padding: 0;
        }
    #parent{
        width: 200px;
        height: 200px;
        background-color: red;
        position: relative;
        top: 100px;
        left: 10px;
        }
    #son{
        width: 100px;
        height: 100px;
        background-color: green;
        position: absolute;
        top: 50px;
        left: 50px;
    }
</style>
var parent = document.getElementById('parent');
var son = document.getElementById('son');

// console.log(parent.offsetParent); //body
// console.log(parent.offsetTop);    //100

// console.log(son.offsetParent);   //parent
console.log(son.offsetTop);         //50

1.parent(div)使用position:relative,此时son(div)的offsetParent为parent(div);如果没有relative,则son(div)的offsetParent为body。son(div)的offsetTop值为该元素顶部相对于父元素parent(div)顶部的长度。

2.parent(div)的offsetParent为body;parent(div)的offsetTop值为该元素顶部相对于父元素body顶部的长度。

PS:offsetTop是该元素顶部相对于父元素顶部的距离而言,所以在使用的时候,要注意父元素是谁。

3.如果想获得该元素相对于整个页面顶部的长度,但父元素不是body,此时的解决方案如下:

function offsetTop(element) {
    var top = element.offsetTop;
    var parent = element.offsetParent;
    while (parent !== null) {
        top += parent.offsetTop;
        parent = parent.offsetParent;
    }
    return top;
}

十八、事件

事件是用户会浏览器自身执行的某些操作,比如用户在单击某个页面中的按钮时,其实也是点击了某个页面
事件流:描述的是从页面中接受事件的顺序
事件流的两种方式:事件冒泡、事件捕获
事件冒泡:事件一开始从最具体的元素接收,然后逐级向上传播到较为不具体的节点(IE9+、FF、Chrome、Safari则将事件一直冒泡到Window对象)
事件捕获:一开始从不太具体的节点接收到事件,而最具体的节点应该最后接收到事件(IE9+、FF、Chrome、Safari则从Window对象开始接收)
事件处理程序:响应某个事件的函数

HTML事件处理程序

<input type="button" value="Click Me" onclick="showMessage()"/>
function showMessage(){
    alert('hello world');
}

缺点:
1. HTML与JavaScript代码紧密耦合,导致如果要改变事件处理程序,需要改动两处
2. 存在时差问题,如果函数showMessage()在用户点击按钮之前还没有被完全解析,就会引发错误
3. 这样的扩展事件处理程序的作用域链在不同的浏览器中会导致不同的结果

Dom0级事件处理程序

<input type="button" id="btn" value="Click Me"/>
window.onload = function(){
    var btn = document.getElementById('btn');
    btn.onclick = function(){
        alert('hello');
    };
}

缺点:对于每一个事件只能支持一个事件处理程序函数

Dom2级事件处理程序

<input type="button" id="btn" value="Click Me"/>
//添加事件
function addEvent(element,type,fn){
    if(element.addEventListener){
        //false代表冒泡阶段调用事件处理程序
        element.addEventListener(type,fn,false); 
    }else{
        element.attachEvent('on'+type,fn);
    }
}

//移除事件
function removeEvent(element,type,fn){
    if(element.removeEventListener){
        element.removeEventListener(type,fn,false);
    }else{
        element.detachEvent('on'+type,fn);
    }
}

window.onload = function(){
    var btn = document.getElementById('btn');
    addEvent(btn,'click',function(){
        alert('hello');
    });
};

使用Dom2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序

检测浏览器是否支持DOM2级事件:var isSupportted = document.implementation.hasFeature("MouseEvents","2.0");

注:通过addEventListener()添加的事件处理程序只能通过removeEventListener()来移除,并且移除时传入的参数与添加处理程序使用的参数相同,意味着通过addEventListener()添加的匿名函数将无法移除。
缺点:IE添加事件的方法仍存在缺陷,比如在使用attachEvent()的情况下,事件处理程序会在全局作用域中允许,因此this等于window。

btn.attachEvent('onclick',function(){
    alert(this === window);  //true
});

再比如这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。

btn.attachEvent('onclick',function(){
    alert('111');
});
btn.attachEvent('onclick',function(){
    alert('222');
});
//输出:222  111

具体解决方法:

JavaScript 跨浏览器添加移除事件

十九、值类型与引用类型赋值区别

值类型:string、number、boolean、undefined
引用类型:object(Array、Date、Function、null等)

值类型变量:存储的是数据本身
引用类型变量:存储的是数据在内存中的地址,数据单独存储在内存中


值类型的赋值:直接将存储的数据复制一份赋值给另一个变量,两份数据在内存中是完全独立的

var str1 = "hello";
var str2 = str1;
console.log(str2);   // hello
str2 = "world";     
console.log(str2);  //world
console.log(str1);  //hello

引用类型的赋值:将变量中存储的地址复制给另一个变量,但是两个变量共享同一个对象,即修改其中一个对象,另外一个也会发生变化

var obj1 = {  //变量名obj1保存的只是指向该对象的引用
    name : "suoz",
    age : 20
}; 
var obj2 = obj1;
console.log(obj2.age);  // 20
obj2.age = 22;
console.log(obj2.age);  // 22
console.log(obj1.age);  // 22

注意:

var obj1 = {
    name : "suoz",
    age : 20    
};

function change(obj2){
    //相当于var obj2 = new Object();  obj2 = obj1;
    obj2.age = 22;
    //相当于var obj2 = new Object();  指向另外一个新对象 此时obj2与obj1无关系
    obj2 = {
        age : 24
    };
    obj2.age = 26;
}

change(obj1);
console.log(obj1.age);    // 22

二十、函数声明的三种方式

基本函数声明

function test(){
    alert('hello');
}

函数表达式

var test = function(){
    alert('hello');
}

new 函数

var test = new Function();

二十一、try-catch-finally 异常处理语句块

异常特征之一:一旦发生异常,后面的代码不会执行

try{
    //可能出现的代码
    //无法捕获语法错误
}catch(e){
    //出现错误后执行的代码
}finally{
    //不管出不出现错误,这里的代码始终执行
    //一般做释放资源的操作
}

注:手动抛出异常 throw

二十二、函数封装与对象封装

函数封装
优点:使用函数封装,使得代码复用性更好

function setStyle(elements){
    for(var i=0; i<elements.length; i++){
        var element = elements[i];
        element.style.border = "1px solid #eee";
    }
}

对象封装
优点:
1. 暴露在全局的只有一个对象名,避免全局变量的污染
2. 使用对象将代码进行功能模块的划分,有利于维护

var object = {
    setStyle : function(elements){
        for(var i=0; i<elements.length; i++){
            var element = elements[i];
            element.style.border = "1px solid #eee";
        }
    }
};

二十三、创建对象的几种方式

跳转至 JavaScript创建对象的几种方式

二十四、原型

前提须知:JavaScript内部一切皆对象,即函数也是对象
原型:在创建一个构造函数时,函数内部有一个原型(prototype)属性,该属性指向一个对象,该对象就是原型对象。
注:默认的原型对象内部有一个constructor属性,指向该构造函数

原型的作用:原型对象中的成员(属性和方法)都可以被使用,以及被该构造函数实例化出来的所有对象所共享

原型对象的使用:
1. 使用对象的动态特性,为原型对象添加属性

Person.prototype.sayHello = function(){
    console.log('hello');
};
  1. 直接替换原型对象
Person.prototype = {
    "sayheloo" : function(){
        console.log('hello');
    },
};

注:
1. 直接替换原型对象,会导致 替换之前创建的对象 与 替换之后创建的对象 的原型对象不一致
2. 在新替换的原型中,没有constructor属性,会影响三角结构关系(构造函数-实例对象-原型对象)的合理性,因此在新替换的原型中,手动添加constructor属性,以保证三角关系

Person.prototype = {
    "constructor" : Person,
};
  1. 如果在原型对象中有引用类型的属性,实例化出来的多个对象,只要其中一个对象修改该属性内容,其它对象该属性的内容也会发生变化
var arr = [1,2,3]
Person.prototype.arr = arr;
var obj1 = new Person();
var obj2 = new Person();
console.log(obj2.arr);  //1,2,3
obj1.arr.push(4) 
console.log(obj1.arr);  //1,2,3,4
console.log(obj2.arr);  //1,2,3,4

属性的查找:
1. 使用对象访问属性的时候,会先在对象的实例属性中查找,如果找到了就直接使用
2. 如果没有找到,就去原型对象的属性中查找

proto属性:通过构造函数实例化出来的每个对象都有一个proto属性,该属性可以直接访问该构造函数的原型对象,由于这个属性不是标准属性,所以存在通用性问题,一般不推荐使用该属性。

var obj = new Person();
console.log(obj.__proto__);  //Person.prototype

二十五、eval() 和 JSON.parse()

eval():可以用来将json格式字符串转化为js代码,并执行

var str = "var a = 10";
eval(str);      //将字符串解析为js代码并且运行
console.log(a);  // 10

注:
1. 使用eval()解析json格式字符串的时候,会将{}解析为代码块而不是对象字面量

var obj = '{ "name":"suoz","age":20 }';
eval(obj);  //错误
//1.在JSON格式的字符串前面拼接上"var o ="
eval("var o = " + obj);
console.log(o);
//2.在JSON格式的字符串用()括起来,就不会将{}解析为代码块,而是解析为表达式
eval("(" + obj + ")"); //正确方法二
console.log(obj);
  1. 使用eval()有安全性问题,因此不推荐使用
    var str = 'prompt("请输入内容")';
    eval(str);
    //弹出的窗口中输入location.href="http://www.baidu.com";
    //会自动运行代码,自动跳转到百度页面

JSON.parse:也是将JSON格式字符串解析为Js代码,但不会执行

var obj = '{ "name":"suoz","age":20 }';
JSON.parse(obj);
console.log(obj);

二十六、变量、函数提升

定义:提升就是将变量或函数的声明提升至作用域的顶部

JS中代码分为两个阶段:预解析阶段 和 执行阶段
预解析阶段:在执行代码之前先执行通过var声明的变量和函数声明

alert(foo);     // undefined
var foo = 2;

/*执行顺序相当于
    var foo;   //预解析阶段
    alert(foo);
    foo = 2;   //执行阶段
*/

函数重名:最后的函数会覆盖前面的函数

foo();   // 222

function foo(){
    alert(111);
}

function foo(){
    alert(222);
}

foo();  //222

/*执行顺序相当于
    function foo(){ alert(111); }
    function foo(){ alert(222); }
    alert(foo);
    alert(foo);
*/

变量和函数重名:在JS中,提升的时候,如果变量与函数重名,那么会忽略掉变量,只提升函数

alert(foo);   //函数体 function foo(){}
function foo(){}
var foo = 2;
alert(foo);   // 2

/*执行顺序相当于
    function foo(){}
    //var foo;  只提升函数
    alert(foo);  //函数体
    foo = 2;
    alert(foo);  //2
*/

变量和函数提升的练习:

var foo = 1;
function test(){
    if(!foo){
        var foo = 10;
    }
    alert(foo);
}
test();

/*执行顺序相当于
    var foo;
    function test(){
        var foo;
        if(!foo){
            foo = 10;
        }
        alert(foo);   //10
    }
    foo = 1;
    test(); //此处才执行test()函数体内的代码
*/

二十七、async/defer/src

<script>标签的属性:

async:异步,可以表示当前脚本必须立即加载,但不会影响页面中的其它资源的加载,不能保证各个脚本的加载顺序;

defer:延迟加载,设置该属性的脚本可以等页面全部加载完毕再加载该脚本,IE4~IE7支持,其它浏览器支持HTML5,则该属性不在有效;

src:引入外部脚本文件;

为什么有时候将js脚本文件放在</body>标签前面,而不是<head></head>标签内部?

放在head标签内部意味着当加载完内部资源后才开始加载、显示页面(页面的显示通常是在body标签之后的代码),如果当js文件数量过多,体积过大时,需要等待js文件加载完毕才去显示页面,在加载js文件过程中,页面是一片空白的,显示假死状态。因此将js脚本文件放在```</body>```前面,可以等待页面加载完毕,才去加载相应的js脚本资源。

二十八、如何精准的获取文字的宽度和高度

<span id="span">hello world!</span>

平时如果获取一个文字的width和height值,都是用以下的方法 或 是直接通过自己写的兼容性的getStyle()来获取非行间样式的值

var span = document.getElementById("span");
alert(span.offsetWidth);    //79
alert(span.offsetHeight);   

今天在知乎上意外看到通过getBoundingClientRect()能获取更准确的数值

var rect = span.getBoundingClientRect();
var width = rect.right - rect.left;     //78.6484375
// 元素下边距离页面上边的距离 - 元素上边距离页面上边的距离
var height = rect.bottom - rect.top;

注:这个方法在IE浏览器中获取的话默认坐标是从(2,2)开始的,而在其他浏览器中是从(0,0)开始的,因此我们需要做兼容性处理,使得各个浏览器的样式统一。

function getRect(element){
    var top = document.documentElement.clientTop;
    var left = document.documentElement.clientLeft;
    var rect = element.getBoudingClientRect();

    return {
        top : rect.top - top,
        right : rect.right - left,
        bottom : rect.bottom - top,
        left : rect.left - left
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值