测试标题

js篇

==js基本数据类型==

5中基本数据类型:null、undefined、string、number、booleanjavascript

==setTimeout和setInterval==

《JavaScript高级程序设计》这本书里面,介绍了不少关于setTimeout函数的神奇使用,今天来介绍下第一个——使用setTimeout代替setInterval进行间歇调用。css

“在开发环境下,不多使用间歇调用(setInterval),缘由是后一个间歇调用极可能在前一个间歇调用结束前启动”。html

  这话怎么理解呢?前端

  首先咱们来看一下通常状况下的setInterval函数的使用,以及如何使用setTimeout代替setIntervaljava

var executeTimes = 0;
var intervalTime = 500;
var intervalId = null;

// 放开下面的注释运行setInterval的Demo
intervalId = setInterval(intervalFun,intervalTime);
// 放开下面的注释运行setTimeout的Demo
// setTimeout(timeOutFun,intervalTime);

function intervalFun(){
    executeTimes++;
    console.log("doIntervalFun——"+executeTimes);
    if(executeTimes==5){
        clearInterval(intervalId);
    }
}

function timeOutFun(){
    executeTimes++;
    console.log("doTimeOutFun——"+executeTimes);
    if(executeTimes<5){
        setTimeout(arguments.callee,intervalTime);
    }
}

  代码比较简单,咱们只是在setTimeout的方法里面又调用了一次setTimeout,就能够达到间歇调用的目的。node

  重点来了,为何做者建议咱们使用setTimeout代替setInterval呢?setTimeout式的间歇调用和传统的setInterval间歇调用有什么区别呢?jquery

  •   区别在于,setInterval间歇调用,是在前一个方法执行前,就开始计时,好比间歇时间是500ms,那么无论那时候前一个方法是否已经执行完毕,都会把后一个方法放入执行的序列中。这时候就会发生一个问题,假如前一个方法的执行时间超过500ms,加入是1000ms,那么就意味着,前一个方法执行结束后,后一个方法立刻就会执行,由于此时间歇时间已经超过500ms了。
var executeTimes = 0;
var intervalTime = 500;
var intervalId = null;
var oriTime = new Date().getTime();

// 放开下面的注释运行setInterval的Demo
// intervalId = setInterval(intervalFun,intervalTime);
// 放开下面的注释运行setTimeout的Demo
setTimeout(timeOutFun,intervalTime);

function intervalFun(){
    executeTimes++;
    var nowExecuteTimes = executeTimes;
    var timeDiff = new Date().getTime() - oriTime;
    console.log("doIntervalFun——"+nowExecuteTimes+", after " + timeDiff + "ms");
    var delayParam = 0;
    sleep(1000);
    console.log("doIntervalFun——"+nowExecuteTimes+" finish !");
    if(executeTimes==5){
        clearInterval(intervalId);
    }
}

function timeOutFun(){
    executeTimes++;
    var nowExecuteTimes = executeTimes;
    var timeDiff = new Date().getTime() - oriTime;
    console.log("doTimeOutFun——"+nowExecuteTimes+", after " + timeDiff + "ms");
    var delayParam = 0;
    sleep(1000);
    console.log("doTimeOutFun——"+nowExecuteTimes+" finish !");
    if(executeTimes<5){
        setTimeout(arguments.callee,intervalTime);
    }
}

function sleep(sleepTime){
    var start=new Date().getTime();
    while(true){
        if(new Date().getTime()-start>sleepTime){
            break;    
        }
    }
}

  (这里使用大牛提供的sleep函数来模拟函数运行的时间)css3

  执行setInterval的Demo方法,看控制台web

doIntervalFun——1, after 500ms
VM2854:19 doIntervalFun——1 finish !
VM2854:16 doIntervalFun——2, after 1503ms
VM2854:19 doIntervalFun——2 finish !
VM2854:16 doIntervalFun——3, after 2507ms
VM2854:19 doIntervalFun——3 finish !
VM2854:16 doIntervalFun——4, after 3510ms
VM2854:19 doIntervalFun——4 finish !
VM2854:16 doIntervalFun——5, after 4512ms
VM2854:19 doIntervalFun——5 finish !

  能够发现,fun2和fun1开始的间歇接近1000ms,恰好就是fun1的执行时间,也就意味着fun1执行完后fun2立刻就执行了,和咱们间歇调用的初衷背道而驰。ajax

  咱们注释掉setInterval的Demo方法,放开setTimeout的Demo方法,运行,查看控制台

doTimeOutFun——1, after 500ms
VM2621:32 doTimeOutFun——1 finish !
VM2621:29 doTimeOutFun——2, after 2001ms
VM2621:32 doTimeOutFun——2 finish !
VM2621:29 doTimeOutFun——3, after 3503ms
VM2621:32 doTimeOutFun——3 finish !
VM2621:29 doTimeOutFun——4, after 5004ms
VM2621:32 doTimeOutFun——4 finish !
VM2621:29 doTimeOutFun——5, after 6505ms
VM2621:32 doTimeOutFun——5 finish !

  这下终于正常了,fun1和fun2相差了1500ms = 1000 + 500,fun2在fun1执行完的500ms后执行。
  

==闭包==

闭包的定义:

在js高级教程中的定义是:有权访问另外一个函数做用域中的变量的函数。通俗地讲,若是一个函数执行完之后,这个函数中还存在一部分在内存当中,没有被垃圾回收机制回收,这个函数就称为闭包。

闭包的做用

1.实现私有变量

若是咱们写一个函数,里面有一个name值,咱们能够容许任何人访问这个name属性,可是只有少部分人,能够修改这个name属性,咱们就能够使用闭包,能够在setName值中,写哪些人具备修改的权限。

var person = function(){   
    //变量做用域为函数内部,外部没法访问,不会与外部变量发生重名冲突   
    var name = "FE";      
    return {
      //管理私有变量   
       getName : function(){   
           return name;   
       },   
       setName : function(newName){   
           name = newName;   
       }   
    }   
};   

2.数据缓存

假如说咱们执行一个计算量很大函数,返回一个值,而这个值在其余函数中还有应用,这种状况下使用闭包,能够将该数据保存在内存中,供其余的函数使用(这是在其余博客中看到的,具体不是很清楚,若是有兴趣,能够本身查阅相关文献)。

缺点:

形成内存消耗过大,若是处理不当,会形成内存泄漏

==事件防抖和事件节流==

  • 事件防抖:debounce的做用是在让在用户动做中止后延迟x ms再执行回调。
  • 事件节流:throttle的做用是在用户动做时没隔必定时间(如200ms)执行一次回调。

使用缘由:

浏览器的一些事件,如:resize,scroll,keydown,keyup,keypress,mousemove等。这些事件触发频率太过频繁,绑定在这些事件上的回调函数会不停的被调用。这样浏览器的目的是为了保证信息的一致性,而对于咱们来讲就是一种资源的浪费了。

代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>test</title>

</head>
<style>
    #mydiv{
        border-bottom: 1px solid;
        width: 100%;
        height: 300px;
        background-color: #999999;
    }
    .mouseMoveDisplay{
        border-top: 1px solid rosybrown;
        width: 100%;
        height: 200px;
        background-color: #bbbbbb;

    }
</style>
<body>
<div id="mydiv"></div>
<table class="mouseMoveDisplay">
    <tr>
        <th>
            一共移动了<span class="mouseMove all">0</span>次
        </th>

    </tr>

    <tr>
        <th>
            防抖移动了<span class="mouseMove debounce">0</span>次
        </th>

    </tr>

    <tr>
        <th>
            节流移动了<span class="mouseMove throttle">0</span>次
        </th>
    </tr>




</table>

<script>
    //防抖模式
    function debounceFunction(fn,delay){

        var delay=delay||200;
        var timer;
        return function(){
            var th=this;
            var args=arguments;
            if (timer) {
                clearTimeout(timer);
            }
            timer=setTimeout(function () {
                timer=null;
                fn.apply(th,args);
            }, delay);
        };
    }


    //事件节流
    function throttleFunction(fn,interval){
        var last;
        var timer;
        var interval=interval||200;
        return function(){
            var th=this;
            var args=arguments;
            var now=+new Date();
            if(last&&now-last<interval){
                clearTimeout(timer);
                timer=setTimeout(function(){
                    last=now;
                    fn.apply(th,args);
                },interval);
            }else{
                last=now;
                fn.apply(th,args);
            }
        }
    }

    var mydiv=document.getElementById("mydiv");

    //全部的移动次数
    var allCount = 0;//全部的次数
    var all = document.getElementsByClassName('all')[0];
    mydiv.addEventListener("mousemove",function () {
        allCount++;
        all.innerHTML = allCount;
    })


    //防抖的移动次数
    var debounceCount = 0;//全部的次数
    var debounce = document.getElementsByClassName('debounce')[0];
    mydiv.addEventListener("mousemove",debounceFunction(function () {
        debounceCount++;
        debounce.innerHTML = debounceCount;
    }))


    //节流的移动次数
    var throttleCount = 0;//全部的次数
    var throttle = document.getElementsByClassName('throttle')[0];
    mydiv.addEventListener("mousemove",throttleFunction(function () {
        throttleCount++;
        throttle.innerHTML = throttleCount;
    }))





</script>
</body>

</html>

==数组中的forEach和map的区别==

大多数状况下,咱们都要对数组进行遍历,而后常常用到的两个方法就是forEach和map方法。
先来讲说它们的共同点

相同点

  • 都是循环遍历数组中的每一项
  • forEach和map方法里每次执行匿名函数都支持3个参数,参数分别是item(当前每一项),index(索引值),arr(原数组)
  • 匿名函数中的this都是指向window
  • 只能遍历数组
  • 都不会改变原数组

区别

==map方法==

1.map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
2.map方法不会对空数组进行检测,map方法不会改变原始数组。
3.浏览器支持:chrome、Safari1.5+、opera都支持,IE9+,

array.map(function(item,index,arr){},thisValue)

var arr = [0,2,4,6,8];
var str = arr.map(function(item,index,arr){
    console.log(this); //window
    console.log("原数组arr:",arr); //注意这里执行5次
    return item/2;
},this);
console.log(str);//[0,1,2,3,4]

若arr为空数组,则map方法返回的也是一个空数组。

==forEach方法==

1.forEach方法用来调用数组的每一个元素,将元素传给回调函数
2.forEach对于空数组是不会调用回调函数的。

Array.forEach(function(item,index,arr){},this)
var arr = [0,2,4,6,8];
var sum = 0;
var str = arr.forEach(function(item,index,arr){
    sum += item;
    console.log("sum的值为:",sum); //0 2 6 12 20
    console.log(this); //window
},this)
console.log(sum);//20
console.log(str); //undefined

不管arr是否是空数组,forEach返回的都是undefined。这个方法只是将数组中的每一项做为callback的参数执行一次。

==for in和for of的区别==

遍历数组一般使用for循环,ES5的话也能够使用forEach,ES5具备遍历数组功能的还有map、filter、some、every、reduce、reduceRight等,只不过他们的返回结果不同。可是使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数。

Array.prototype.method=function(){
  console.log(this.length);
}
var myArray=[1,2,4,5,6,7]
myArray.name="数组"
for (var index in myArray) {
  console.log(myArray[index]);
}

使用for in 也能够遍历数组,可是会存在如下问题:

1.index索引为字符串型数字,不能直接进行几何运算

2.遍历顺序有可能不是按照实际数组的内部顺序

3.使用for in会遍历数组全部的可枚举属性,包括原型。例如上栗的原型方法method和name属性

因此for in更适合遍历对象,不要使用for in遍历数组。

那么除了使用for循环,如何更简单的正确的遍历数组达到咱们的指望呢(即不遍历method和name),ES6中的for of更胜一筹.

Array.prototype.method=function(){
  console.log(this.length);
}
var myArray=[1,2,4,5,6,7]
myArray.name="数组";
for (var value of myArray) {
  console.log(value);
}

记住,for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。

for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name

遍历对象 一般用for in来遍历对象的键名

Object.prototype.method=function(){
  console.log(this);
}
var myObject={
  a:1,
  b:2,
  c:3
}
for (var key in myObject) {
  console.log(key);
}

for in 能够遍历到myObject的原型方法method,若是不想遍历原型方法和属性的话,能够在循环内部判断一下,==hasOwnPropery==方法能够判断某属性是不是该对象的实例属性

for (var key in myObject) {
  if(myObject.hasOwnProperty(key)){
    console.log(key);
  }
}

一样能够经过ES5的Object.keys(myObject)获取对象的实例属性组成的数组,不包括原型方法和属性。

Object.prototype.method=function(){
  console.log(this);
}
var myObject={
  a:1,
  b:2,
  c:3
}
Object.keys(myObject).forEach(function(key,index){  
    console.log(key,myObject[key])
    
})

==实现EventEmitter方法==

class EventEmitter {
    constructor() {
        this.events = {};
    }

    on(eventName, fn) {
        let fnList = this.events[eventName] || [];
        fnList.push(fn)
        if (eventName) {
            this.events[eventName] = fnList;
        }
    }

    emit(eventName, ...agr) {
        let funcs = this.events[eventName];
        if (funcs && funcs.length) {
            for (let j = 0; j < funcs.length; j++) {
                funcs[j](...agr);
            }
        }
    }
    off(eventName, fn) {
        let funcs = this.events[eventName];
        if (fn) {
            this.events[eventName].splice(fn, 1);
        } else {
            delete this.events[eventName]
        }
    }
}

==let、var、const区别==

var

第一个就是做用域的问题,var不是针对一个块级做用域,而是针对一个函数做用域。举个例子:

function runTowerExperiment(tower, startTime) {
  var t = startTime;

  tower.on("tick", function () {
    ... code that uses t ...
  });
  ... more code ...
}

这样是没什么问题的,由于回调函数中能够访问到变量t,可是若是咱们在回调函数中再次命名了变量t呢?

function runTowerExperiment(tower, startTime) {
  var t = startTime;

  tower.on("tick", function () {
    ... code that uses t ...
    if (bowlingBall.altitude() <= 0) {
      var t = readTachymeter();
      ...
    }
  });
  ... more code ...
}

后者就会将前者覆盖。

第二个就是循环的问题。
看下面例子:

var messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];

for (var i = 0; i < messages.length; i++) {
    setTimeout(function () {
        document.write(messages[i]);
    },i*1500);
}

输出结果是:undefined
由于for循环后,i置为3,因此访问不到其值。

let

为了解决这些问题,ES6提出了let语法。let能够在{},if,for里声明,其用法同var,可是做用域限定在块级。可是javascript中不是没有块级做用域吗?这个咱们等会讲。还有一点很重要的就是let定义的变量==不存在变量提高==。

变量提高
这里简单提一下什么叫作变量提高。

var v='Hello World'; 
(function(){ 
    alert(v); 
    var v='I love you'; 
})()

上面的代码输出结果为:undefined。

为何会这样呢?这就是由于变量提高,变量提高就是把变量的声明提高到函数顶部,好比:

(function(){ 
    var a='One'; 
    var b='Two'; 
    var c='Three'; 
})()

实际上就是:

(function(){ 
    var a,b,c; 
    a='One'; 
    b='Two'; 
    c='Three'; 
})()

因此咱们刚才的例子其实是:

var v='Hello World'; 
(function(){ 
    var v;
    alert(v); 
    v='I love you'; 
})()

因此就会返回undefined啦。

这也是var的一个问题,而咱们使用let就不会出现这个问题。由于它会报语法错误:

{     
    console.log( a );   // undefined
    console.log( b );   // ReferenceError!      
    var a;
    let b;     
}

再来看看let的块级做用域。

function getVal(boo) {
    if (boo) {
        var val = 'red'
        // ...
        return val
    } else {
        // 这里能够访问 val
        return null
    }
    // 这里也能够访问 val
}

而使用let后:

function getVal(boo) {
    if (boo) {
        let val = 'red'
        // ...
        return val
    } else {
        // 这里访问不到 val
        return null
    }
    // 这里也访问不到 val
}

一样的在for循环中:

function func(arr) {
    for (var i = 0; i < arr.length; i++) {
        // i ...
    }
    // 这里访问获得i
}

使用let后:

function func(arr) {
    for (let i = 0; i < arr.length; i++) {
        // i ...
    }
    // 这里访问不到i
}

也就是说,==let只能在花括号内部起做用==。

const

再来讲说const,==const表明一个值的常量索引==。

const aa = 11;
alert(aa) //11
aa = 22;
alert(aa) //11

可是常量的值在垃圾回收前永远不能改变,因此须要谨慎使用。

还有一条须要注意的就是和其余语言同样,==常量的声明必须赋予初值==。即便咱们想要一个undefined的常量,也须要声明:

const a = undefined;

块级做用域
最后提一下刚才说到的块级做用域。

在以前,javascript是没有块级做用域的,咱们都是经过()来模拟块级做用域。

(function(){
 //这里是块级做用域 
})();

可是在ES6中,{}就能够直接代码块级做用域。因此{}内的内容是不能够在{}外访问获得的。

咱们能够看看以下代码:

if (true) {
    function foo() {
        document.write( "1" );
    }
}
else {
    function foo() {
        document.write( "2" );
    }
}

foo();      // 2

在咱们所认识的javascript里,这段代码的输出结果为2。这个叫作函数声明提高,不只仅提高了函数名,也提高了函数的定义。若是你基础不扎实的话,能够看看这篇文章:深刻理解javascript之IIFE

可是在ES6里,这段代码或抛出ReferenceErroe错误。由于{}的块级做用域,致使外面访问不到foo(),也就是说函数声明和let定义变量同样,都被限制在块级做用域中了。

==事件循环==

从promise、process.nextTick、setTimeout出发,谈谈Event Loop中的Job queue

简要介绍:谈谈promise.resove,setTimeout,setImmediate,process.nextTick在EvenLoop队列中的执行顺序

1.问题的引出

event loop都不陌生,是指主线程从“==任务队列==”中循环读取任务,好比

例1:

setTimeout(function(){console.log(1)},0);

console.log(2)

//输出2,1

在上述的例子中,咱们明白首先执行主线程中的同步任务,当主线程任务执行完毕后,再从event loop中读取任务,所以先输出2,再输出1。

event loop读取任务的前后顺序,取决于任务队列(Job queue)中对于不一样任务读取规则的限定。好比下面一个例子:

例2:

setTimeout(function () {
  console.log(3);
}, 0);

Promise.resolve().then(function () {
  console.log(2);
});
console.log(1);
//输出为  1  2 3

先输出1,没有问题,由于是同步任务在主线程中优先执行,这里的问题是setTimeout和Promise.then任务的执行优先级是如何定义的。

2 . Job queue中的执行顺序

在Job queue中的队列分为两种类型:==macro-task和microTask==。咱们举例来看执行顺序的规定,咱们设

macro-task队列包含任务: a1, a2 , a3
micro-task队列包含任务: b1, b2 , b3

执行顺序为,首先执行marco-task队列开头的任务,也就是 a1 任务,执行完毕后,在执行micro-task队列里的全部任务,也就是依次执行b1, b2 , b3,执行完后清空micro-task中的任务,接着执行marco-task中的第二个任务,依次循环。

了解完了macro-task和micro-task两种队列的执行顺序以后,咱们接着来看,真实场景下这两种类型的队列里真正包含的任务(咱们以node V8引擎为例),在node V8中,这两种类型的真实任务顺序以下所示:

macro-task(宏任务)队列真实包含任务:

script(主程序代码),setTimeout, setInterval, setImmediate, I/O, UI rendering

micro-task(微任务)队列真实包含任务:

process.nextTick, Promises, Object.observe, MutationObserver

由此咱们获得的执行顺序应该为:

==script(主程序代码)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering==

在ES6中macro-task队列又称为ScriptJobs,而micro-task又称PromiseJobs

3 . 真实环境中执行顺序的举例

(1) setTimeout和promise

例3:

setTimeout(function () {
  console.log(3);
}, 0);

Promise.resolve().then(function () {
  console.log(2);
});

console.log(1);

咱们先以第1小节的例子为例,这里遵循的顺序为:

script(主程序代码)——>promise——>setTimeout 
对应的输出依次为:1 ——>2————>3

(2) process.nextTick和promise、setTimeout

例子4:

setTimeout(function(){console.log(1)},0);

new Promise(function(resolve,reject){
   console.log(2);
   resolve();
}).then(function(){console.log(3)
}).then(function(){console.log(4)});

process.nextTick(function(){console.log(5)});

console.log(6);
//输出2,6,5,3,4,1

这个例子就比较复杂了,这里要注意的一点在定义promise的时候,promise构造部分是同步执行的,这样问题就迎刃而解了。

首先分析Job queue的执行顺序:

==script(主程序代码)——>process.nextTick——>promise——>setTimeout==

I) 主体部分: 定义promise的构造部分是同步的,
所以先输出2 ,主体部分再输出6(同步状况下,就是严格按照定义的前后顺序)

II)process.nextTick: 输出5

III)promise: 这里的promise部分,严格的说实际上是promise.then部分,输出的是3,4

IV) setTimeout : 最后输出1

综合的执行顺序就是: 2——>6——>5——>3——>4——>1

(3)更复杂的例子

setTimeout(function(){console.log(1)},0);

new Promise(function(resolve,reject){
   console.log(2);
   setTimeout(function(){resolve()},0)
}).then(function(){console.log(3)
}).then(function(){console.log(4)});

process.nextTick(function(){console.log(5)});

console.log(6);

//输出的是  2 6 5 1 3 4

种状况跟咱们(2)中的例子,区别在于promise的构造中,没有同步的resolve,所以promise.then在当前的执行队列中是不存在的,只有promise从pending转移到resolve,才会有then方法,而这个resolve是在一个setTimout时间中完成的,所以3,4最后输出。

==浏览器内核==

  1. IE浏览器内核:Trident内核,也是俗称的IE内核;
  2. Chrome浏览器内核:统称为Chromium内核或Chrome内核,之前是Webkit内核,如今是Blink内核;
  3. Firefox浏览器内核:Gecko内核,俗称Firefox内核;
  4. Safari浏览器内核:Webkit内核;
  5. Opera浏览器内核:最初是本身的Presto内核,后来是Webkit,如今是Blink内核;
  6. 360浏览器、猎豹浏览器内核:IE+Chrome双内核;
  7. 搜狗、遨游、QQ浏览器内核:Trident(兼容模式)+Webkit(高速模式);
  8. 百度浏览器、世界之窗内核:IE内核;
  9. 2345浏览器内核:之前是IE内核,如今也是IE+Chrome双内核;

==懒加载的原理==

  • 原理:先将img标签中的src连接设为同一张图片(空白图片),将其真正的图片地址存储再img标签的自定义属性中(好比data-src)。当js监听到该图片元素进入可视窗口时,即将自定义属性中的地址存储到src属性中,达到懒加载的效果。
  • 这样作能防止页面一次性向服务器响应大量请求致使服务器响应慢,页面卡顿或崩溃等问题。

代码实现

既然懒加载的原理是基于判断元素是否出如今窗口可视范围内,首先咱们写一个函数判断元素是否出如今可视范围内:

function isVisible($node){
    var winH = $(window).height(),
        scrollTop = $(window).scrollTop(),
        offSetTop = $(window).offSet().top;
    if (offSetTop < winH + scrollTop) {
        return true;
    } else {
        return false;
    }
}

再添加上浏览器的事件监听函数,让浏览器每次滚动就检查元素是否出如今窗口可视范围内:

$(window).on("scroll", function{
    if (isVisible($node)){
        console.log(true);
    }
})

咱们已经很接近了,如今咱们要作的是,让元素只在第一次被检查到时打印true,以后就再也不打印了

var hasShowed = false;
$(window).on("sroll",function{
    if (hasShowed) {
        return;
    } else {
        if (isVisible($node)) {
            hasShowed = !hasShowed;
            console.log(true);
        }
    }
})

咦,咱们好像已经实现了懒加载。

懒加载实例

  • 1.简单懒加载:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        img{
            display: block;
            max-height: 300px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>懒加载页面</h1>
        <img src="1.png" data-src='1.jpg' alt="">
        <img src="1.png" data-src='2.jpg' alt="">
        <img src="1.png" data-src='3.jpg' alt="">
        <img src="1.png" data-src='4.jpg' alt="">
        <img src="1.png" data-src='5.jpg' alt="">
        <img src="1.png" data-src='6.jpg' alt="">
        <img src="1.png" data-src='7.jpg' alt="">
        <img src="1.png" data-src='8.jpg' alt="">
        <img src="1.png" data-src='9.jpg' alt="">
    </div>
</body>
</html>
<script>
    var scrollTop = window.scrollY;
    var imgs = Array.from(document.querySelectorAll('img'));

    lazyLoad();

    window.onscroll = () => {
        scrollTop = window.scrollY;
        lazyLoad(); 
    }
    function lazyLoad(){
        imgs.forEach((item,index)=>{
            if( item.offsetTop < window.innerHeight + scrollTop ){
                console.log(item.offsetTop)
                item.setAttribute('src',item.dataset.src)
            }
        })
    }
</script>

这里有坑请注意!!! 并且这个问题很差百度 - . -
若是复制上面的代码,首次加载进页面发现全部图片均已经加载完毕,没有实现懒加载的效果
由于函数调用时img.onload没有完成,img元素没有高度!!!
解决办法是在外层套一个window.onload

window.onload = function(){
        lazyLoad();
}
  • 2.函数节流throttle懒加载

推荐!!!高频滚动模式下, 每隔一段时间才会实现渲染~~
实现原理是 加入一个开关变量, 控制每隔固定的一段时间,函数才可能被触发~

window.onload = function(){
    var scrollTop = window.scrollY;
    var imgs = Array.from(document.querySelectorAll('img'));

    lazyLoad();

    //函数节流模式
    var canRun = true;
    window.onscroll = () => {
        if( !canRun ){
            return 
        }
        canRun = false;
        setTimeout(()=>{
            scrollTop = window.scrollY;
            lazyLoad();
            canRun = true;
        },1000)

    }

    function lazyLoad(){
        imgs.forEach((item,index)=>{
            if( item.offsetTop < window.innerHeight + scrollTop ){
                console.log(item.offsetTop)
                item.setAttribute('src',item.dataset.src)
            }
        })
    }
}

为了逻辑清晰 , 打包成函数调用:

window.onload = function(){

    var scrollTop = window.scrollY;
    var imgs = Array.from(document.querySelectorAll('img'));
    lazyLoad();
    let canRun = true;//开关变量用于函数节流
    window.addEventListener('scroll',throttle(lazyLoad,500));



    //定义懒加载函数 , 从上到下懒加载 , 从下到上也是懒加载
    function lazyLoad(){
        imgs.forEach((item,index)=>{
            if( scrollTop===0 && item.dataset.src !== '' && item.offsetTop < window.innerHeight + scrollTop ){
                alert()
                item.setAttribute('src',item.dataset.src)
                item.setAttribute('data-src','')
            }else if( item.dataset.src !== '' && item.offsetTop < window.innerHeight + scrollTop && item.offsetTop > scrollTop ){
                item.setAttribute('src',item.dataset.src)
                item.setAttribute('data-src','')
            }
        })
    }


    //定义函数节流函数
    function throttle(fun,delay){ 
        return function(){
            // fun();
            if( !canRun ){
                return 
            }
            console.log('!!!')
            canRun = false;
            setTimeout(()=>{
                scrollTop = window.scrollY;
                fun(imgs);
                canRun = true
            },delay)
        }
    }

}
  • 3.函数防抖debounce

原理是设置clearTimeout和setTimeout,的dalayTime控制一个事件若是频繁触发,将只会执行最近的一次… 能够用在用户注册时候的手机号码验证和邮箱验证。只有等用户输入完毕后,前端才须要检查格式是否正确,若是不正确,再弹出提示语。如下仍是以页面元素滚动监听的例子

效果:一直滑动的时候,将不会有图片加载, 停下后300ms会加载

window.onload = function(){
    var scrollTop = window.scrollY;
    var imgs = Array.from(document.querySelectorAll('img'));

    lazyLoad();

    //函数防抖模式
    var timer = null;
    window.onscroll = () => {
        clearTimeout(timer);
        timer = setTimeout(()=>{
            scrollTop = window.scrollY;
            lazyLoad();
        },300)      
    }

    function lazyLoad(){
        imgs.forEach((item,index)=>{
            if( item.offsetTop < window.innerHeight + scrollTop ){
                console.log(item.offsetTop)
                item.setAttribute('src',item.dataset.src)
            }
        })
    }
}
  • 4.最终版 throttle + debounce

完美懒加载
注意点: 在滚动条下拉状态下刷新页面, 页面实现更新渲染以后会立马触发滚动条事件,回到上一次页面的停留点,可是并非从scrollTop为0的位置出发~

window.onload = function(){

    var scrollTop = window.scrollY;
    var imgs = Array.from(document.querySelectorAll('img'));
    lazyLoad();
    // 采用了节流函数
    window.addEventListener('scroll',throttle(lazyLoad,500,1000));

    function throttle(fun, delay, time) {
        var timeout,
            startTime = new Date();
        return function() {

            var context = this,
                args = arguments,
                curTime = new Date();
            clearTimeout(timeout);
            // 若是达到了规定的触发时间间隔,触发 handler
            console.log(curTime - startTime)
            if (curTime - startTime >= time) {
                fun();
                startTime = curTime;
                // 没达到触发间隔,从新设定定时器
            } else {
                timeout = setTimeout(fun, delay);
            }
        };
    };
    // 实际想绑定在 scroll 事件上的 handler
    // 须要访问到imgs ,  scroll 
    function lazyLoad(){
        scrollTop = window.scrollY;
        imgs.forEach((item,index)=>{
            if( scrollTop===0 && item.dataset.src !== '' && item.offsetTop < window.innerHeight + scrollTop ){
                // alert()
                item.setAttribute('src',item.dataset.src)
                item.setAttribute('data-src','')
            }else if( item.dataset.src !== '' && item.offsetTop < window.innerHeight + scrollTop && item.offsetTop > scrollTop ){
                item.setAttribute('src',item.dataset.src)
                item.setAttribute('data-src','')
            }
        })
    }

}

==typeof和instanceof==

ECMAScript是松散类型的,一次须要一种手段来检测给定变量的数据类型,typeof操做符(注意不是函数哈!)就是负责提供这方面信息的,

typeof 能够用于检测基本数据类型和引用数据类型。

语法格式以下:

typeof variable
返回6种String类型的结果:
  • "undefined" - 若是这个值未定义
  • "boolean" - 若是这个值是布尔值
  • "string" - 若是这个值是字符串
  • "number" - 若是这个值是数值
  • "object" - 若是这个值是对象或null
  • "function" - 若是这个值是函数

示例:

console.log(typeof 'hello'); // "string"
console.log(typeof null); // "object"
console.log(typeof (new Object())); // "object"
console.log(typeof(function(){})); // "function"

typeof主要用于检测基本数据类型:数值、字符串、布尔值、undefined, 由于typeof用于检测引用类型值时,==对于任何Object对象实例(包括null),typeof都返回"object"值,没办法区分是那种对象,对实际编码用处不大。==

instanceof 用于判断一个变量是否某个对象的实例

在检测基本数据类型时typeof是很是得力的助手,但在检测引用类型的值时,这个操做符的用处不大,一般,咱们并非想知道某个值是对象,而是想知道它是什么类型的对象。此时咱们能够使用ECMAScript提供的instanceof操做符。

语法格式以下:

result = variable instanceof constructor

返回布尔类型值:

  • true - 若是变量(variable)是给定引用类型的实例,那么instanceof操做符会返回true
  • false - 若是变量(variable)不是给定引用类型的实例,那么instanceof操做符会返回false

示例:

function Person(){}
    function Animal(){}
    var person1 = new Person();
    var animal1 = new Animal();
    console.log(person1 instanceof Person); // true
    console.log(animal1 instanceof Person); // false
    console.log(animal1 instanceof Object); // true
    console.log(1 instanceof Person);   //false


    var oStr =  new String("hello world");
    console.log(typeof(oStr));      // object
    console.log(oStr instanceof String);
    console.log(oStr instanceof Object);
 
    // 判断 foo 是不是 Foo 类的实例
 
    function Foo(){}
    var foo = new Foo();
    console.log(foo instanceof Foo);
 
    // instanceof 在继承中关系中的用法
    console.log('instanceof 在继承中关系中的用法');
 
 
    function Aoo(){}
    function Foo(){}
 
    Foo.prototype = new Aoo();
    var fo = new Foo();
 
    console.log(fo instanceof Foo);
    console.log(fo instanceof Aoo)

根据规定,全部引用类型的值都是Object的实例。所以,在检测一个引用类型值和Object构造函数时,instanceof操做符会始终返回true。若是使用instanceof 操做符检测基本类型值时,该操做符会始终返回false,由于基本类型不是对象。

console.log(Object.prototype.toString.call(null));
// [object Null]
undefined
console.log(Object.prototype.toString.call([1,2,3]));
//[object Array]
undefined
console.log(Object.prototype.toString.call({}));
// [object Object]

==原型和原型链==

参考js高级教程

==数组的使用方法==

push、pop、shift、unshift、splice、join、reverse、sort、slice、map every some fliter forEach、reduce.....
做为最经常使用的类型,JavaScript中的数组仍是和其余语言中有很大的区别的。
主要体如今两点:

  • 数组中的每一项均可以保存任何类型的数据
  • 数组的大小能够动态调整

首先来介绍建立数组的两种方法

  1. 第一种方式
var arr1 = new Array();

var arr2 = new Array(3);

var arr3 = new Array('jerry');

能够看到这种方式创建数组,arr1是一个空数组,arr2是一个长度为3的数组,arr3是一个包含‘jerry’一个元素的数组。同时经过这种方式建立的数组,new操做符能够省略。

  1. 第二种方式称为数组字面量表示法。
var a = [];
var arr = ['tom','jack']

数组的长度是可动态调整,致使咱们直接就能够设置它的长度

var a = [123,423];
a.length = 10;
a[9]='123';
console.log(a[8])//undefined

a[10] = '123'
console.log(a.length)//10

从上面的代码中咱们能够看出:

  • 若是咱们设置的长度大于原来的数组的长度的时候, 数组后面的元素自动设置为undefined。
  • 若是咱们对大于当前数组长度的位置赋值的时候,那么就会致使数组的长度自动变为你所赋值位置+1.

2016103149463arr_function.png

改变数组的方法

栈方法

pop和push很简单,也很容易理解。pop就是从数组的末尾删除一个元素并返回。push是在数组的末尾添加一个元素。

var arr = [1,3,4];
arr.pop();
console.log(arr);//[1,3]

arr.push(5);
console.log(arr);//[1,3,5]

队列方法

shift和unshift是和栈方法是相对的,它俩是从数组的头部进行操做。shift是从头部删除一个元素,unshift是从同步加入一个元素。

var arr = [1,3,4];
arr.shift();
console.log(arr);//[3,4]

arr.unshift(5);
console.log(arr);//[5,3,4]

重排序方法

reverse是对数组进行翻转。

var arr = [1,3,4];
arr.reverse();
console.log(arr);//[4,3,1]

sort是对数组进行排序。

var arr = [1,3,5,4];
arr.sort();
console.log(arr);//[1,3,4,5];

sort默认的对数组进行升序排序。sort能够接收一个自定义的比较函数,自定义排序规则。

sort方法会调用每一个元素的toString()方法,从而经过字符串进行比较大小。即便是数值,依然要变换成字符串,从而就会带来一些问题。好比

var arr = [1,3,15,4];
arr.sort()
console.log(arr);//[1,15,3,4];

转换为字符串以后,‘15’是排在‘3’,‘4’的前面的。这就带来了问题,因此在进行数值数组的排序,必须进行自定义排序规则。

var arr = [1,3,15,4];
function compare(v1,v2){
    if(v1 > v2)
        return 1;
    if(v1 < v2)
        return -1;
    return 0;
}
arr.sort(compare)
console.log(arr);//[1,3,4,15]

splice方法

splice方法能够说是数组中功能最强大的方法,集多项功能于一身。主要的用途就是用来向数组的中部插入元素。

splice方法主要有三种用法。

splice的返回值为删除的元素组成的数组。若是删除的元素为空,返回空数组。

  • 删除元素

splice(index,count),index表示删除的位置,count表示删除的项数。

var arr = [1,3,4];
console.log(arr.splice(2,1));//[4]
//删除元素
console.log(arr);[1,3];
  • 插入元素

splice(index,0,element,....)
index 表示要插入的位置,0表明删除0个元素,element要插入的元素,若是要插入多个元素,能够继续添加。

var arr = [1,3,4];
console.log(arr.splice(2,0,'tom'));//[ ]

console.log(arr);//[1,3,'tom',4]

若是index的值大于数组自己的长度,那么就在最后位置添加。且数组的长度只会加1.

var arr = [1,3,4];
console.log(arr.splice(5,0,'tom'));//[ ]

console.log(arr);//[1,3,4,'tom']
console.log(arr.length);//4

若是index的值为负数,那么就从(arr.length+index)位置开始插入,若是(arr.length+index)的值小于0,那么就从数组的开始位置进行插入。

var arr = [1,3,4,4,7,6];
console.log(arr.splice(-1,0,'tom'));//[ ]

console.log(arr);//[1,3,4,4,7,'tom',6]
console.log(arr.length);//7

console.log(arr.splice(-7,0,'tom'));//[ ]

console.log(arr);//['tom',1,3,4,4,7,'tom',6]
console.log(arr.length);//8

console.log(arr.splice(-10,0,'jack'));//[ ]

console.log(arr);//['jack','tom',1,3,4,4,7,'tom',6]
console.log(arr.length);//9
  • 替换元素

splice(index,count,element,....).index表明替换开始的位置,count > 0,element表示要替换成的元素。其实替换过程包含两个过程:1.删除. 2插入.也就是上面的两个过程的融合。

var arr = [1,3,4];
console.log(arr.splice(1,1,'tom'));//[3]

console.log(arr);//[1,'tom',4]

若是index大于数组的长度,或者小于0,处理的结果同上面插入元素处理的方式同样。

不改变数组的方法

转换方法

join方法主要是用来将数组的元素经过规定的方式链接成字符串。

var arr = [1,3,4,5];
console.log(arr.join(','))//1,3,4,5
console.log(arr.join('+'))//1+3+4+5
console.log(arr.join('?'))//1?3?4?5
console.log(arr)//[1,3,4,5]

操做方法

slice和concat方法。
slice方法主要用来返回指定位置的数组的子数组。slice(start,end)。end省略,返回的是从开始位置到数组的末尾。end不省略,返回的是从start到end之间的子数组,包括start位置但不包括end位置的数组。

var arr = [1,3,4,5];

console.log(arr.slice(1));//[3,4,5]
console.log(arr.slice(1,2));//[3]

若是slice方法的参数中有一个负数,则用数组长度加上该数来肯定相应的位置。例如在一个长度为5的数组上调用slice(-2,-1)与调用slice(3,4)获得的结果相同。若是结束位置小于起始位置,则返回空数组。

concat 方法,主要是链接多个数组。

var arr = [1,3,4,5];
var testArr = [1,23,4];
console.log(arr.concat(testArr));//[1,3,4,5,1,23,4]
console.log(arr.concat('tom'));//[1,3,4,5,'tom']

迭代方法

ES5新增长的迭代方法主要包括以下几种

map
every
some
fliter
forEach

这几个方法有一下共同点,都接收两个参数,一个是要在数组上每一项运行的函数,一个是运行该函数做用域的对象,改变this的指向(可选)。其中函数须要传入三个参数,一个是每一个元素的值,每一个元素的index,数组自己。

function(value,index,array)
{
}

下面一个一个的来介绍

  • map

map返回数组中每个数组元素通过传入的函数处理后组成的新数组

var arr = [1,3,4];
var newArr = arr.map(function(value,index,array){
    return value*2;
})
console.log(newArr);//[2,6,8]
console.log(arr);//[1,3,4]
  • some和every

some和every比较相像。some是对每个数组中的元素运行传入的函数,若是有一个返回true,那么就返回true;every是对每个数组中的元素运行传入的函数,若是全部的都返回true,那么就返回true。

var arr = [1,3,4];
var result1 = arr.some(function(value,index,array){
    return value > 2;
})

var result2 = arr.every(function(value,index,array){
    return value > 2;
})
console.log(result1);// true
console.log(result2);// false
  • filter

从名字能够看出,这是一个过滤的方法,返回的一个数组,这个数组是知足传入的参数的函数的元素所组成的。

var arr = [1,3,4];
var result = arr.filter(function(value,index,array){
    return value > 2;
})
console.log(result);// [3,4]
  • forEach

forEach主要用来遍历,遍历数组中每个元素,对其进行操做。该方法没有返回值。

var arr = [1,3,4];
arr.forEach(function(value,index,array){
    console.log('arr['+index+']='+value);
})
// 结果
arr[0]=1
arr[1]=3
arr[2]=4

缩小方法

reduce和reduceRight.这两个方法接收两个参数,一个是每项都运行的函数,一个是缩小基础的初始值(可选)。reduce和reduceRight返回的是一个值。其中每项都运行的函数包含四个参数,

funciton(prev,cur,index,array){
}

下面经过一个例子就能够说明这个函数是干吗的。

var arr = [1,3,4];
var result = arr.reduce(function(prev,cur,index,array){
    return prev+cur;
},10);
console.log(result)//18
var result1 = arr.reduce(function(prev,cur,index,array){
    return prev+cur;
});
console.log(result1)//8
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
参数                                    描述
function(total,currentValue, index,arr)    必需。用于执行每一个数组元素的函数。
total                                    必需。初始值, 或者计算结束后的返回值。
currentValue                            必需。当前元素
currentIndex                            可选。当前元素的索引
arr                                        可选。当前元素所属的数组对象。
initialValue                            可选。传递给函数的初始值
var arr = [11,22,33,44,55,66];
    var arr1 = arr.reduce(function(total, currentValue, currentIndex, arr){
        return total+"-"+currentValue;
    },"00");
    console.log(arr1);//00-11-22-33-44-55-66
var arr = [11,22,33,44,55,66];
    var arr1 = arr.reduceRight(function(total, currentValue, currentIndex, arr){
        return total+"-"+currentValue;
    },"00");
    console.log(arr1);//00-66-55-44-33-22-11

reduceRight和reduce同样,无非他开始的位置是从数组的后面。

其余方法

  • indexOf()
  • lastIndexOf()

这两个主要是用来判断元素在数组中的位置,未找到返回-1,接收两个参数,indexOf(searchElement[, fromIndex]),lastIndexOf(searchElement[, fromIndex])。fromIndex可选。其中formIndex也能够指定字符串。

var arr = [1,3,4,4,1,5,1];
var value = arr.indexOf(1)
console.log(value)//0
value = arr.indexOf(1,4)
console.log(value)//4
value = arr.indexOf(1,5)
console.log(value)//6

value = arr.lastIndexOf(1)
console.log(value)//6



value = arr.lastIndexOf(1,3)
console.log(value)//0
  • toString()
  • toLocalString()
  • valueOf()

这三个方法是全部对象都具备的方法。

toString()返回的是一个字符串,toLocaleString同它相似。valueOf()返回的是一个数组

var arr= [1,3,4]
console.log(arr.toString());//1,3,4
console.log(arr.valueOf());//[1,3,4]
console.log(arr.toLocaleString());//1,3,4

能够复写toString(),toLocaleString()返回不一样的结果。

==callee和caller==

1 :caller 返回一个调用当前函数的引用 若是是由顶层调用的话 则返回null

(举个栗子哈 ==caller给你打电话的人 == 谁给你打电话了 谁调用了你 很显然是下面a函数的执行 只有在打电话的时候你才能知道打电话的人是谁 因此对于函数来讲 只有caller在函数执行的时候才存在)

var callerTest = function() {

           console.log(callerTest.caller) ;  

     } ;

      function a() {

           callerTest() ;   

     }

     a() ;//输出function a() {callerTest();}

     callerTest() ;//输出null

2 :callee 返回一个正在被执行函数的引用 (这里经常使用来递归匿名函数自己 可是在严格模式下不可行)

callee是arguments对象的一个成员 表示对函数对象自己的引用 它有==个length属性(表明形参的长度)==

var c = function(x,y) {

             console.log(arguments.length,arguments.callee.length,arguments.callee)

      } ;

     

     c(1,2,3) ;//输出3 2 function(x,y) {console.log(arguments.length,arguments.callee.length,arguments.callee)}

==new的执行原理==

一、建立一个新对象;[var o = new Object();]

二、将构造函数的做用域赋给新对象(所以this指向了这个新对象);

三、执行构造函数中的代码(为这个新对象添加属性);

四、返回新对象。

==get和post的区别==

get请求传参长度的误区

误区:咱们常常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。

实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,咱们必须再次强调下面几点:

HTTP 协议 未规定 GET 和POST的长度限制
GET的最大长度显示是由于 浏览器和 web服务器限制了 URI的长度
不一样的浏览器和WEB服务器,限制的最大长度不同
要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度 8182byte

补充get和post请求在缓存方面的区别

补充补充一个get和post在缓存方面的区别:

  • get请求相似于查找的过程,用户获取数据,能够不用每次都与数据库链接,因此能够使用缓存。
  • post不一样,post作的通常是修改和删除的工做,因此必须与数据库交互,因此不能使用缓存。所以get请求适合于请求缓存。

HTTP 协议中GET和POST到底有哪些区别

HTTP 定义了与服务器交互的不一样方法,最经常使用的有4种,Get、Post、Put、Delete,若是我换一下顺序就好记了,Put(增),Delete(删),Post(改),Get(查),即增删改查,下面简单叙述一下:

  • 1)Get, 它用于获取信息,注意,他只是获取、查询数据,也就是说它不会修改服务器上的数据,从这点来说,它是数据安全的,而稍后会提到的Post它是能够修改数据的,因此这也是二者差异之一了。
  • 2) Post,它是能够向服务器发送修改请求,从而修改服务器的,比方说,咱们要在论坛上回贴、在博客上评论,这就要用到Post了,固然它也是能够仅仅获取数据的。
  • 3)Delete 删除数据。能够经过Get/Post来实现。用的很少,暂很少写,之后扩充。
  • 4)Put,增长、放置数据,能够经过Get/Post来实现。用的很少,暂很少写,之后扩充。

下面简述一下Get和Post区别:

1) GET请求的数据是放在HTTP包头中的,也就是URL以后,一般是像下面这样定义格式的,(而Post是把提交的数据放在HTTP正文中的)。

login.action?name=hyddd&password=idontknow&verify=%E4%BD%E5%A5%BD
  • a,以 ? 来分隔URL和数据;
  • b,以& 来分隔参数;
  • c,若是数据是英文或数字,原样发送;
  • d,若是数据是中文或其它字符,则进行BASE64编码。

2)GET提交的数据比较少,最多1024B,由于GET数据是附在URL以后的,而URL则会受到不一样环境的限制的,好比说IE对其限制为2K+35,而POST能够传送更多的数据(理论上是没有限制的,但通常也会受不一样的环境,如浏览器、操做系统、服务器处理能力等限制,IIS4可支持80KB,IIS5可支持100KB)。

3)Post的安全性要比Get高,由于Get时,参数数据是明文传输的,并且使用GET的话,还可能形成Cross-site request forgery攻击。而POST数据则能够加密的,但GET的速度可能会快些。

因此综上几点,总结成下表:

操做方式 数据位置 明文密文 数据安全 长度限制 应用场景
GET http包头 明文 不安全 长度较小 查询数据
POST http正文 可明可密 安全 支持较大的数据传输 修改数据

==点击li可以实现弹出当前li索引==

经典的js问题 实现点击li可以弹出当前li索引与innerHTML的函数

按照咱们日常的想法,代码应该是这样写的:

var myul = document.getElementsByTagName("ul")[0];
    var list = myul.getElementsByTagName("li");
 
    function foo(){
        for(var i = 0, len = list.length; i < len; i++){
            list[i].onclick = function(){
                alert(i + "----" + this.innerHTML);
            }
        }
    }
    foo();

可是不巧的是产生的结果是这样的:

索引index为何老是4呢,这是js中没有块级做用域致使的。这里有三种解决思路

  1. 使用闭包
<script type="text/javascript">
 
    var myul = document.getElementsByTagName("ul")[0];
    var list = myul.getElementsByTagName("li");
 
    function foo(){
        for(var i = 0, len = list.length; i < len; i++){
            var that = list[i];
            list[i].onclick = (function(k){
                var info = that.innerHTML;
                return function(){
                    alert(k + "----" + info);
                };
            })(i);
        }
    }
    foo();
    </script>

2.使用ES6中的新特性let来声明变量

用let来声明的变量将具备块级做用域,很明显能够达到要求,不过须要注意的是得加个'use strict'(使用严格模式)才会生效

<script type="text/javascript">

var myul = document.getElementsByTagName("ul")[0];
var list = myul.getElementsByTagName("li");

function foo(){'use strict'
    for(let i = 0, len = list.length; i < len; i++){
        list[i].onclick = function(){
            alert(i + "----" + this.innerHTML);
        }
    }
}
foo();

</script>

3.事件委托

<script type="text/javascript">

var myul = document.querySelector('ul');
var list = document.querySelectorAll('ul li');

myul.addEventListener('click', function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElemnt;

    for(var i = 0, len = list.length; i < len; i++){
        if(list[i] == target){
            alert(i + "----" + target.innerHTML);
        }
    }
});

</script>

4.引入jquery,使用其中的on或delegate进行事件绑定(它们都有事件代理的特性)

<script type="text/javascript" src="jquery-1.8.2.min.js"></script>

<script type="text/javascript">

$("ul").delegate("li", "click", function(){
    var index = $(this).index();
     var info = $(this).html();
    alert(index + "----" + info);
});

</script>

<script type="text/javascript">

$("ul").on("click", "li", function(){
    var index = $(this).index();
     var info = $(this).html();
    alert(index + "----" + info);
});

</script>

==js添加事件-兼容各类环境==

JS中添加事件 兼容各类环境

var EventUtil = {
    //添加
    addHandler : function (element , type, handler {
        if ( element.addEventListener){
            element.addEventListener(type, handler, false);
        }else if ( element.attachEvent) {
            element.attachEvent("on"+type,handler);
        }else {
            element["on" + type] = handler;
        }
    },
    //移除
    removeHandler : function (element , type , handler){
        if(element.removeEventListener){
            element.removeEventListener(type , handler , false);
        }else if(element.detachEvent){
            element.detachEvent("on" + type , handler);
        }else{
            element["on" + type] = handler;
        }
    }
}

==this指向的问题==

  首先必需要说的是,this的指向在函数定义的时候是肯定不了的,只有函数执行的时候才能肯定this到底指向谁,实际上this的最终指向的是==那个调用它的对象==(这句话有些问题,后面会解释为何会有问题,虽然网上大部分的文章都是这样说的,虽然在不少状况下那样去理解不会出什么问题,可是实际上那样理解是不许确的,因此在你理解this的时候会有种琢磨不透的感受),那么接下来我会深刻的探讨这个问题。

例子1:

function a(){
    var user = "追梦子";
    console.log(this.user); //undefined
    console.log(this); //Window
}
a();

按照咱们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就能够证实。

function a(){
    var user = "追梦子";
    console.log(this.user); //undefined
    console.log(this);  //Window
}
window.a();

和上面代码同样吧,其实alert也是window的一个属性,也是window点出来的。

例子2:

var o = {
    user:"追梦子",
    fn:function(){
        console.log(this.user);  //追梦子
    }
}
o.fn();

  这里的this指向的是对象o,由于你调用这个fn是经过o.fn()执行的,那天然指向就是对象o,这里再次强调一点,this的指向在函数建立的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁,必定要搞清楚这个。

其实例子1和例子2说的并不够准确,下面这个例子就能够推翻上面的理论。

若是要完全的搞懂this必须看接下来的几个例子

例子3:

var o = {
    user:"追梦子",
    fn:function(){
        console.log(this.user); //追梦子
    }
}
window.o.fn();

  这段代码和上面的那段代码几乎是同样的,可是这里的this为何不是指向window,若是按照上面的理论,最终this指向的是调用它的对象,这里先说个而外话,window是js中的全局对象,咱们建立的变量其实是给window添加属性,因此这里能够用window点o对象。

  这里先不解释为何上面的那段代码this为何没有指向window,咱们再来看一段代码。

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //12
        }
    }
}
o.b.fn();

  这里一样也是对象o点出来的,可是一样this并无执行它,那你确定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不许确,接下来我将补充一句话,我相信你就能够完全的理解this的指向的问题。

  •   状况1:若是一个函数中有this,可是它没有被上一级的对象所调用,那么this指向的就是window,这里须要说明的是在js的严格版中this指向的不是window,可是咱们这里不探讨严格版的问题,你想了解能够自行上网查找。
  •   状况2:若是一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
  •   状况3:若是一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,例子3能够证实,若是不相信,那么接下来咱们继续看几个例子。
var o = {
    a:10,
    b:{
        // a:12,
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
o.b.fn();

尽管对象b中没有属性a,这个this指向的也是对象b,由于this只会指向它的上一级对象,无论这个对象中有没有this要的东西。

还有一种比较特殊的状况,例子4:

var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); //undefined
            console.log(this); //window
        }
    }
}
var j = o.b.fn;
j();

这里this指向的是window,是否是有些蒙了?实际上是由于你没有理解一句话,这句话一样相当重要。

  this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子4中虽然函数fn是被对象b所引用,可是在将fn赋值给变量j的时候并无执行因此最终指向的是window,这和例子3是不同的,例子3是直接执行了fn。

  this讲来说去其实就是那么一回事,只不过在不一样的状况下指向的会有些不一样,上面的总结每一个地方都有些小错误,也不能说是错误,而是在不一样环境下状况就会有不一样,因此我也没有办法一次解释清楚,只能你慢慢地的去体会。

构造函数版this:

function Fn(){
    this.user = "追梦子";
}
var a = new Fn();
console.log(a.user); //追梦子

  这里之因此对象a能够点出函数Fn里面的user是由于new关键字能够改变this的指向,将这个this指向对象a,为何我说a是对象,由于用了new关键字就是建立一个对象实例,理解这句话能够想一想咱们的例子3,咱们这里用变量a建立了一个Fn的实例(至关于复制了一份Fn到对象a里面),此时仅仅只是建立,并无执行,而调用这个函数Fn的是对象a,那么this指向的天然是对象a,那么为何对象a中会有user,由于你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

  除了上面的这些之外,咱们还能够自行改变this的指向,关于自行改变this的指向请看JavaScript中call,apply,bind方法的总结这篇文章,详细的说明了咱们如何手动更改this的指向。

更新一个小问题当this碰到return时

function fn()  
{  
    this.user = '追梦子';  
    return {};  
}
var a = new fn;  
console.log(a.user); //undefined

再看一个

function fn()  
{  
    this.user = '追梦子';  
    return function(){};
}
var a = new fn;  
console.log(a.user); //undefined

再来

function fn()  
{  
    this.user = '追梦子';  
    return 1;
}
var a = new fn;  
console.log(a.user); //追梦子
function fn()  
{  
    this.user = '追梦子';  
    return undefined;
}
var a = new fn;  
console.log(a.user); //追梦子

什么意思呢?

  ==若是返回值是一个对象,那么this指向的就是那个返回的对象,若是返回值不是一个对象那么this仍是指向函数的实例。==

function fn()  
{  
    this.user = '追梦子';  
    return undefined;
}
var a = new fn;  
console.log(a); //fn {user: "追梦子"}

 还有一点就是虽然null也是对象,可是在这里this仍是指向那个函数的实例,由于null比较特殊。

function fn()  
{  
    this.user = '追梦子';  
    return null;
}
var a = new fn;  
console.log(a.user); //追梦子

知识点补充:

  1.在严格版中的默认的this再也不是window,而是undefined。

  2.new操做符会改变函数this的指向问题,虽然咱们上面讲解过了,可是并无深刻的讨论这个问题,网上也不多说,因此在这里有必要说一下。

function fn(){
    this.num = 1;
}
var a = new fn();
console.log(a.num); //1

  为何this会指向a?首先new关键字会建立一个空的对象,而后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

2017-09-15 11:49:14

  注意: 当你new一个空对象的时候,js内部的实现并不必定是用的apply方法来改变this指向的,这里我只是打个比方而已.

  if (this === 动态的可改变的) return true;
  

==tcp三次握手==

第一次

第一次握手:创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP链接成功)状态,完成三次握手。

==数组去重==

* 数组去重【Array原型扩展一个方法实现数组去重】,利用hash对象

1. 遍历,数组下标去重法
时间复杂度:O(n^2),indexOf自己也消耗了O(n)的复杂度,
空间复杂度:O(n)
IE8如下不支持indexOf
Array.prototype.removeRepeat1 = function() {
    var res =[this[0]];
    for(var i=1; i<this.length;i++){ //从第二项开始遍历
        if(this.indexOf(this[i])==i){
            res.push(this[i]);
        }
    }
    return res;
};
2. 遍历,比较备用数组去重法
Array.prototype.removeRepeat2 = function() {
    var res =[];
    for(var i=0; i<this.length;i++){
        if(res.indexOf(this[i])==-1){
            res.push(this[i]);
         }
    }
    return res;
};
3. 遍历,hash去重法
相似于,利用对象的属性不能相同的特色进行去重
时间复杂度:O(n),空间复杂度:O(n)
Array.prototype.removeRepeat3 = function() {
    var h= {};  //哈希表
    var res = [];
    for(var i=0; i<this.length;i++){
        if(!h[this[i]]){    //若是hash表中没有当前项
            h[this[i]]=true; //存入hash表
            res.push(this[i]);
        }
    }
    return res;
};
4. 遍历,Set去重法(ES6的 Set)
时间复杂度:O(n),
空间复杂度:O(n)
Set兼容性很差,IE11如下不支持
Array.prototype.removeRepeat4 = function(){
    var result = new Set();
    for(var i=0; i<this.length; i++){
        result.add(this[i]);
    }
    return result;
}


//Set的方法二:
Array.from(array)把Set转化为数组
    Array.prototype.removeRepeat41 = function(){     
        return Array.from(new Set(this));;
}
5. 排序后相邻去重法
Array.prototype.removeRepeat5 = function() {
    this.sort();
    var res=[this[0]];
    for(var i = 1; i< this.length; i++){
        if(this[i]!=this[i-1]){
            res.push(this[i]);
        }
    }
    return res;
}

==apply、call、bind区别==

call apply bind

第一个传的参数都是对象,不能传入构造函数,构造函数的typeof是function

nullundefined时,将是JS执行环境的全局变量。浏览器中是window,其它环境(如node)则是global

call方法

语法:call(thisObj,Object)

定义:调用一个对象的一个方法,以另外一个对象替换当前对象。

说明:call 方法能够用来代替另外一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。 
若是没有提供 thisObj 参数,那么 Global 对象被用做 thisObj。

apply方法

语法:apply(thisObj,[argArray])

定义:应用某一对象的一个方法,用另外一个对象替换当前对象。

说明:若是 argArray 不是一个有效的数组或者不是 arguments 对象,那么将致使一个 TypeError。
若是没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用做 thisObj, 而且没法被传递任何参数。

  • 相同点
调用函数时,改变当前传入的对象为函数中this指针的引用

当第一个参数thisObj传入null/undefined的时候将执行js全局对象浏览器中是window,其余环境是global。

  • 不一样点:
call, apply方法区别是,从第二个参数起, call方法参数将依次传递给借用的方法做参数, 而apply直接将这些参数放到一个数组中再传递, 最后借用方法的参数列表是同样的.

bind相同点和call apply相同

而bind是返回一个新函数,这个函数的上下文,为传入的对象。须要再次调用才能时候用.

apply、call、bind比较

那么 apply、call、bind 三者相比较,之间又有什么异同呢?什么时候使用 apply、call,什么时候使用 bind 呢。简单的一个栗子:

ar obj = {

x: 81,

};


var foo = {

getX: function() {

return this.x;

}

}


console.log(foo.getX.bind(obj)()); //81

console.log(foo.getX.call(obj)); //81

console.log(foo.getX.apply(obj)); //81

三个输出的都是81,可是注意看使用 bind() 方法的,他后面多了对括号。

也就是说,区别是,当你但愿改变上下文环境以后并不是当即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会当即执行函数。

==再总结一下==:

  • apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
  • apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
  • apply 、 call 、bind 三者均可以利用后续参数传参;
  • bind是返回对应函数,便于稍后调用;apply、call则是当即调用 。

==深拷贝和浅拷贝==

JavaScript有两种数据类型,基础数据类型和引用数据类型。基础数据类型都是按值访问的,咱们能够直接操做保存在变量中的实际的值。而引用类型如Array,咱们不能直接操做对象的堆内存空间。引用类型的值都是按引用访问的,即保存在变量对象中的一个地址,该地址与堆内存的实际值相关联。

1、深拷贝和浅拷贝的区别

  • 浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象自己,新旧对象共享一块内存;
  • 深拷贝(deep copy):复制并建立一个一摸同样的对象,不共享内存,修改新对象,旧对象保持不变。
var a = 25;
var b = a;
b = 10;
console.log(a);//25
console.log(b);//10

//浅拷贝

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = obj1;
obj2.b = 40;
console.log(obj1);// { a: 10, b: 40, c: 30 } 
console.log(obj2);// { a: 10, b: 40, c: 30 }

//深拷贝

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 40;
console.log(obj1);// { a: 10, b: 20, c: 30 }
console.log(obj2);// { a: 10, b: 40, c: 30 }

2、浅拷贝的实现

var json1 = {"a":"name","arr1":[1,2,3]}
function copy(obj1) {
    var obj2 = {};
    for (var i in obj1) {
      obj2[i] = obj1[i];
    }
    return obj2;
}
var json2 = copy(json1);
json1.arr1.push(4);
alert(json1.arr1);  //1234
alert(json2.arr1)  //1234

3、深拷贝的实现

  • 一、Object.assign()
let foo = {
    a: 1,
    b: 2,
    c: {
        d: 1,
    }
}
let bar = {};
Object.assign(bar, foo);
foo.a++;
foo.a === 2 //true
bar.a === 1 //true
foo.c.d++;
foo.c.d === 2 //true
bar.c.d === 1 //false
bar.c.d === 2 //true

Object.assign()是一种能够对==非嵌套对象==进行深拷贝的方法,若是对象中出现嵌套状况,那么其对被嵌套对象的行为就成了普通的浅拷贝。

  • 二、转成JSON

用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。

var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);   // { body: { a: 10 } } 
console.log(obj2);   // { body: { a: 20 } }
console.log(obj1 === obj2);   // false
console.log(obj1.body === obj2.body);   // false

但这种方法的缺陷是会==破坏原型链==,而且没法拷贝属性值为function的属性

  • 三、递归

采用递归的方法去复制拷贝对象

var json1={
    "name":"shauna",
    "age":18,
    "arr1":[1,2,3,4,5],
    "string":'got7',
    "arr2":[1,2,3,4,5],
    "arr3":[{"name1":"shauna"},{"job":"web"}]
};
    
    
var json2={};
function copy(obj1,obj2){
  var obj2=obj2||{};
  for(var name in obj1){
    if(typeof obj1[name] === "object"){ 
      obj2[name]= (obj1[name].constructor===Array)?[]:{}; 
      copy(obj1[name],obj2[name]); 
    }else{
      obj2[name]=obj1[name];  
    }
  }
  return obj2;
}
json2=copy(json1,json2)
json1.arr1.push(6);
alert(json1.arr1);  //123456
alert(json2.arr1);  //12345

==实现一个bind方法==

Function.prototype.mybind = function(context) {
    var self = this;
    var args = [];//保存bind函数调用时传递的参数
    for(var i = 1, len = arguments.length; i< len;i ++) {
        args.push(arguments[i]);
    }
    
    //bind()方法返回值是一个函数
    return function() {
        //哇,新建立的函数传进来的参数能够在这里拿到哎!!
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(context, args.concat(bindArgs))
    }
}

==ajax==

代码

var xmlhttp;
    if (window.XMLHttpRequest){
        //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
        xmlhttp=new XMLHttpRequest();
    }
    else{
        // IE6, IE5 浏览器执行代码
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
    
    xmlhttp.open("GET","/try/ajax/ajax_info.txt",true);
    xmlhttp.send();
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
            document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
        }
    }

优缺点:

  • 优势

    • 经过异步模式,提高用户体验
    • 优化了浏览器和服务器之间的传输,减小了没必要要的数据往返,减小了带宽的使用
    • Ajax在客户端运行,承担了一部分原本由服务器承担的工做,减小了大量用户下的服务器负载
    • Ajax是经过异步通讯实现的局部刷新
  • 缺点

    • ajax不支持浏览器的back按钮
    • 安全问题,Ajax暴露了与服务器交互的细节
    • 对搜索引擎的支持比较弱
    • 破坏了程序的异常机制
    • 不容易调试

==跨域==

一 跨域的缘由

不少朋友不知道为何要跨域 其实跨域请求存在的缘由:因为浏览器的同源策略,即属于不一样域的页面之间不能相互访问各自的页面内容。

那么什么是==同源策略==呢?

简单说来就是同协议,同域名,同端口。

二 跨域的场景

1.域名不一样 www.yangwei.com 和www.wuyu.com 即为不一样的域名)

2.二级域名相同,子域名不一样(www.wuhan.yangwei.com www.shenzheng.yangwei.com 为子域不一样)

3.端口不一样,协议不一样 ( http://www.yangwei.comhttps://www.yangwei.com属于跨...:8888和www.yangwei.con:8080)

三 跨域的方式

1.前端的方式: possMessage,window.name,document.domain,image.src(得不到数据返回),jsonP(script.src后台不配合得不到数据返回),style.href(得不到数据返回)

一.==imge.src==,==script.src==,==style.href== 不受同源策略的影响能够加载其余域的资源,能够用这个特性,向服务器发送数据。最经常使用的就是使用image.src 向服务器发送前端的错误信息。image.src 和style.href 是没法获取服务器的数据返回的,script.src 服务器端配合能够获得数据返回。

二possMessage,window.name,document.domain 是两个窗口直接相互传递数据。

(1)possMessage 是HTML5中新增的,使用限制是 必须得到窗口的window 引用。IE8+支持,firefox,chrome,safair,opera支持

(2)window.name ,在一个页面中打开另外一个页面时,window.name 是共享的,因此能够经过window.name 来传递数据,window.name的限制大小是2M,这个全部浏览器都支持,且没有什么限制。

3) document.domain 将两个页面的document.domain 设置成相同,document.domain 只能设置成父级域名,既能够访问,使用限制:这顶级域名必须相同,document.domain + iframe跨域
此方案仅限主域相同,子域不一样的跨域应用场景。

  • 实现原理:两个页面都经过js强制设置document.domain为基础主域,就实现了同域。

1.)父窗口:(www.domain.com/a.html)

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
    document.domain = 'domain.com';
    var user = 'admin';
</script>

2.)子窗口:(child.domain.com/b.html)

<script>
    document.domain = 'domain.com';
    // 获取父窗口中变量
    alert('get js data from parent ---> ' + window.parent.user);
</script>

2.纯后端方式: CORS,服务器代理

CORS 是w3c标准的方式,经过在web服务器端设置:响应头Access-Control-Alow-Origin 来指定哪些域能够访问本域的数据,ie8&9(XDomainRequest),10+,chrom4 ,firefox3.5,safair4,opera12支持这种方式。

服务器代理,同源策略只存在浏览器端,经过服务器转发请求能够达到跨域请求的目的,劣势:增长服务器的负担,且访问速度慢。
image

前端代码--script.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script>
        // var getvalue = function (data) {
        //     alert(JSON.stringify(data));
        // };
        // var url = "http://127.0.0.1:3000/cors?callback=getvalue";
        // var script = document.createElement('script');
        // script.setAttribute('src', url);
        // document.getElementsByTagName('head')[0].appendChild(script);

        var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

        // 前端设置是否带cookie
        xhr.withCredentials = false;

        xhr.open('get', 'http://127.0.0.1:3000/cors.js?callback=getvalue', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send();

        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                alert(xhr.responseText);
            }
        };


    </script>
    <!--<script src="http://127.0.0.1:3000"></script>-->
</head>
<body>
ffffffffffffffffffffffffffff
</body>
</html>

后端代码--demo2.js:

var express = require('express');
var app = express();
var http = require('http');
var fs = require('fs');
var url = require('url');

app.get('/', function (req, res) {
    res.sendfile(__dirname+"/index.html");
})



app.get('/cors.js', function(req, res) {
    var pathname = url.parse(req.url).pathname;
    console.log("req for " + pathname + " received.");
    fs.readFile(pathname.substr(1), function (err, data) {
        if (err) {
            console.log(err);
            // HTTP 状态码: 404 : NOT FOUND
            // Content Type: text/plain
            res.writeHead(404, {'Content-Type': 'text/html'});
        }else{
            res.header("Access-Control-Allow-Origin", "*"); //设置请求来源不受限制
            res.header("Access-Control-Allow-Headers", "X-Requested-With");
            res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); //请求方式
            res.header("X-Powered-By", ' 3.2.1')
            res.header("Content-Type", "application/json;charset=utf-8");
            var data = {
                name:  ' - server 3001 cors process',
                id: ' - server 3001 cors process'
            }
            console.log(data);
            // "getvalue(data)"
            res.send("getvalue({ name: '5'})");
        }
        //  发送响应数据
        res.end();
    });


     })


var server = app.listen(3000, function () {
    var host = server.address().address
    var port = server.address().port

    console.log("应用实例,访问地址为 http://%s:%s", host, port)

})
// 控制台会输出如下信息
console.log('Server running at http://127.0.0.1:3000/');

3.先后端结合:JsonP

script.src 不受同源策略的限制,因此能够动态的建立script标签,将要请求数据的域写在src 中参数中附带回调的方法,服务器端返回回调函数的字符串,并带参数。

如 script.src="http://www.yangwei.com/?id=001&callback=getInfoCallback,服务器端返回 getInfoCallBack("name:yangwei;age:18") 这段代码会直接执行,在前面定义好getInfoCallBack函数,既能够得到数据并解析。 这种是最长见的方式。
image

前端代码--script.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script>
        var getvalue = function (data) {
            alert(JSON.stringify(data));
        };
        var url = "http://127.0.0.1:3000/person.js?callback=getvalue";
        var script = document.createElement('script');
        script.setAttribute('src', url);
        document.getElementsByTagName('head')[0].appendChild(script);

    </script>
    <!--<script src="http://127.0.0.1:3000"></script>-->
</head>
<body>
ffffffffffffffffffffffffffff
</body>
</html>

后端代码--demo2.js

var express = require('express');
var app = express();
var http = require('http');
var fs = require('fs');
var url = require('url');

app.get('/', function (req, res) {
    res.sendfile(__dirname+"/index.html");
})

app.get('/person.js',function (req,res) {
    var pathname = url.parse(req.url).pathname;

    // 输出请求的文件名
    console.log("req for " + pathname + " received.");
    // fs.readFile(pathname.substr(1), function (err, data) {
    //     if (err) {
    //         console.log(err);
    //         // HTTP 状态码: 404 : NOT FOUND
    //         // Content Type: text/plain
    //         res.writeHead(404, {'Content-Type': 'text/html'});
    //     }else{
    //         // HTTP 状态码: 200 : OK
    //         // Content Type: text/plain
    //         res.writeHead(200, {'Content-Type': 'text/html'});
    //
    //         // 响应文件内容
    //         res.write(data.toString());
    //     }
    //     //  发送响应数据
    //     res.end();
    // });
    res.send("getvalue({ name: '5'})")


})

app.get('/message.js',function (req,res) {
    var pathname = url.parse(req.url).pathname;

    // 输出请求的文件名
    console.log("req for " + pathname + " received.");
    fs.readFile(pathname.substr(1), function (err, data) {
        if (err) {
            console.log(err);
            // HTTP 状态码: 404 : NOT FOUND
            // Content Type: text/plain
            res.writeHead(404, {'Content-Type': 'text/html'});
        }else{
            // HTTP 状态码: 200 : OK
            // Content Type: text/plain
            res.writeHead(200, {'Content-Type': 'text/html'});

            // 响应文件内容
            res.write(data.toString());
        }
        //  发送响应数据
        res.end();
    });



})

app.get('/cors.js', function(req, res) {
    var pathname = url.parse(req.url).pathname;
    console.log("req for " + pathname + " received.");
    fs.readFile(pathname.substr(1), function (err, data) {
        if (err) {
            console.log(err);
            // HTTP 状态码: 404 : NOT FOUND
            // Content Type: text/plain
            res.writeHead(404, {'Content-Type': 'text/html'});
        }else{
            res.header("Access-Control-Allow-Origin", "*"); //设置请求来源不受限制
            res.header("Access-Control-Allow-Headers", "X-Requested-With");
            res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); //请求方式
            res.header("X-Powered-By", ' 3.2.1')
            res.header("Content-Type", "application/json;charset=utf-8");
            var data = {
                name:  ' - server 3001 cors process',
                id: ' - server 3001 cors process'
            }
            console.log(data);
            // "getvalue(data)"
            res.send("getvalue({ name: '5'})");
        }
        //  发送响应数据
        res.end();
    });


     })


var server = app.listen(3000, function () {
    var host = server.address().address
    var port = server.address().port

    console.log("应用实例,访问地址为 http://%s:%s", host, port)

})
// 控制台会输出如下信息
console.log('Server running at http://127.0.0.1:3000/');

==继承的几种方法==

字面量建立

  • 代码
person = {
    name:'FE',
    age:23
}
  • 优缺点

使用同一个接口建立不少对象,会产生大量重复的代码

工厂模式

  • 代码
function creatPerson(name,age,job){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName=function(){
        alert(this.name);
    }
    
    return o;
}


var person1 = creatPerson('FE',20,'teacher');
  • 优缺点

虽然解决了建立类似对象的问题,可是没有解决对象识别的问题(即怎样知道一个对象的类型)

构造函数模式

  • 代码
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName=function(){
        alert(this.name);
    }
    
}


var person1 = Person('FE',20,'teacher');
  • 优缺点

建立自定义函数意味着未来能够将它的实例标识为一种特定的数据类型。可是每一个方法都要在实例上从新建立一遍。

原型模式

  • 代码
function Person(){};
Person.prototype.name = "FE";
Person.prototype.age = 20;
Person.prototype.sayName = function(){
    alert(this.name);
};

var person1 = new Person();
person1.sayName();            //'FE'
  • 优缺点

可让全部的实例共享它所包含的属性和方法。原型中的属性和方法是共享的,可是实例通常要有单独的属性和方法,通常不多单独使用原型模式。

混合模式

  • 代码
function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends=['aa','ss','dd'];
}

Person.prototype.sayName = function(){
    alert(this.name);
}

var person1 = new Person('FE',20,'teacher');
  • 优缺点

构造函数模式定义实例的属性,原型模式定义公共的属性和方法

==建立字面量的几种方法==

原型链继承

  • 定义

利用原型让一个引用类型继承另一个引用类型的属性和方法

  • 代码
function SuperType(){
    this.property = 'true';
}

SuperType.prototype.getSuperValue = function(){
    return this.property;
}

function SubType(){
    this.subProperty = 'false';
}

SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
    return this.subProperty;
}

var instance = new SubType();
alert(instance.getSuperValue());//true
  • 优势

简单明了,容易实现,在父类新增原型属性和方法,子类都能访问到。

  • 缺点

包含引用类型值的函数,全部的实例都指向同一个引用地址,修改一个,其余都会改变。不能像超类型的构造函数传递参数

构造函数继承

  • 定义

在子类型构造函数的内部调用超类型的构造函数

  • 代码
function SuperType(){
    this.colors = ['red','yellow'];
}

function SubType(){
    SuperType.call(this);
}

var instance1 = new SubType();
instance1.colors.push('black');

var instance2 = new SubType();
instance2.colors.push('white');

alert(instance1.colors);//'red','yellow','black'

alert(instance2.colors);//'red','yellow','white'
  • 优势

简单明了,直接继承了超类型构造函数的属性和方法

  • 缺点

方法都在构造函数中定义,所以函数复用就无从谈起了,并且超类型中的原型的属性和方法,对子类型也是不可见的,结果全部的类型只能使用构造函数模式。

组合继承

  • 定义

使用原型链实现多原型属性和方法的继承,使用构造函数实现实例的继承

  • 代码
function SuperType(name){
    this.name = name;
    this.colors = ['red','black'];
}

SuperType.prototype.sayName = function()
{
   alert(this.name); 
}


function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}

SubType.protptype = new SuperType();
SubType.protptype.sayAge = function(){
    alert(this.age);
    
}
  • 优势

解决了构造函数和原型继承中的两个问题

  • 缺点

不管何时,都会调用两次超类型的构造函数

还有其余实现继承的方法,这里就再也不赘述了,可参考js高级教程

==promise==

ES6 Promise 用法讲解
Promise是一个构造函数,本身身上有==all、reject、resolve==这几个眼熟的方法,原型上有==then、catch==等一样很眼熟的方法。

那就new一个

var p = new Promise(function(resolve, reject){
    //作一些异步操做
    setTimeout(function(){
        console.log('执行完成');
        resolve('随便什么数据');
    }, 2000);
});

Promise的构造函数接收一个参数,是函数,而且传入两个参数:resolve,reject,分别表示异步操做执行成功后的回调函数和异步操做执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不许确,按照标准来说,resolve是将Promise的状态置为fulfilled,reject是将Promise的状态置为rejected。不过在咱们开始阶段能够先这么理解,后面再细究概念。

在上面的代码中,咱们执行了一个异步操做,也就是setTimeout,2秒后,输出“执行完成”,而且调用resolve方法。

运行代码,会在2秒后输出“执行完成”。注意!我只是new了一个对象,并无调用它,咱们传进去的函数就已经执行了,这是须要注意的一个细节。因此咱们用Promise的时候通常是包在一个函数中,在须要的时候去运行这个函数,如:

function runAsync(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('执行完成');
            resolve('随便什么数据');
        }, 2000);
    });
    return p;            
}
runAsync()

这时候你应该有两个疑问:1.包装这么一个函数有毛线用?2.resolve('随便什么数据');这是干毛的?

咱们继续来说。在咱们包装好的函数最后,会return出Promise对象,也就是说,执行这个函数咱们获得了一个Promise对象。还记得Promise对象上有then、catch方法吧?这就是强大之处了,看下面的代码:

runAsync().then(function(data){
    console.log(data);
    //后面能够用传过来的数据作些其余操做
    //......
});

在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,而且会拿到咱们在runAsync中调用resolve时传的的参数。运行这段代码,会在2秒后输出“执行完成”,紧接着输出“随便什么数据”。

这时候你应该有所领悟了,原来then里面的函数就跟咱们平时的回调函数一个意思,可以在runAsync这个异步任务执行完成以后被执行。这就是Promise的做用了,简单来说,就是能把原来的回调写法分离出来,在异步操做执行完后,用链式调用的方式执行回调函数。

你可能会不屑一顾,那么牛逼轰轰的Promise就这点能耐?我把回调函数封装一下,给runAsync传进去不也同样吗,就像这样:

function runAsync(callback){
    setTimeout(function(){
        console.log('执行完成');
        callback('随便什么数据');
    }, 2000);
}

runAsync(function(data){
    console.log(data);
});

效果也是同样的,还费劲用Promise干吗。那么问题来了,有多层回调该怎么办?若是callback也是一个异步操做,并且执行完后也须要有相应的回调函数,该怎么办呢?总不能再定义一个callback2,而后给callback传进去吧。而Promise的优点在于,能够在then方法中继续写Promise对象并返回,而后继续调用then来进行回调操做。

链式操做的用法
因此,从表面上看,Promise只是可以简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数可以及时调用,它比传递callback函数要简单、灵活的多。因此使用Promise的正确场景是这样的:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});

这样可以按顺序,每隔两秒输出每一个异步回调中的内容,在runAsync2中传给resolve的数据,能在接下来的then方法中拿到。运行结果以下:

猜猜runAsync一、runAsync二、runAsync3这三个函数都是如何定义的?没错,就是下面这样

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步任务1执行完成');
            resolve('随便什么数据1');
        }, 1000);
    });
    return p;            
}


function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步任务2执行完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('异步任务3执行完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}

在then方法中,你也能够直接return数据而不是Promise对象,在后面的then中就能够接收到数据了,好比咱们把上面的代码修改为这样:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return '直接返回数据';  //这里直接返回数据
})
.then(function(data){
    console.log(data);
});

那么输出就变成了这样:

reject的用法
到这里,你应该对“Promise是什么玩意”有了最基本的了解。那么咱们接着来看看ES6的Promise还有哪些功能。咱们光用了resolve,还没用reject呢,它是作什么的呢?事实上,咱们前面的例子都是只有“执行成功”的回调,尚未“失败”的状况,reject的做用就是把Promise的状态置为rejected,这样咱们在then中就能捕捉到,而后执行“失败”状况的回调。看下面的代码。

function getNumber(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            var num = Math.ceil(Math.random()*10); //生成1-10的随机数
            if(num<=5){
                resolve(num);
            }
            else{
                reject('数字太大了');
            }
        }, 2000);
    });
    return p;            
}

getNumber()
.then(
    function(data){
        console.log('resolved');
        console.log(data);
    }, 
    function(reason, data){
        console.log('rejected');
        console.log(reason);
    }
);

getNumber函数用来异步获取一个数字,2秒后执行完成,若是数字小于等于5,咱们认为是“成功”了,调用resolve修改Promise的状态。不然咱们认为是“失败”了,调用reject并传递一个参数,做为失败的缘由。

运行getNumber而且在then中传了两个参数,then方法能够接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。因此咱们可以分别拿到他们传过来的数据。屡次运行这段代码,你会随机获得下面两种结果:
或者
catch的用法
咱们知道Promise对象除了then方法,还有一个catch方法,它是作什么用的呢?其实它和then的第二个参数同样,用来指定reject的回调,用法是这样:

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

效果和写在then的第二个参数里面同样。不过它还有另一个做用:在执行resolve的回调(也就是上面then中的第一个参数)时,若是抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。请看下面的代码:

getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

在resolve的回调中,咱们console.log(somedata);而somedata这个变量是没有被定义的。若是咱们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。可是在这里,会获得这样的结果:

也就是说进到catch方法里面去了,并且把错误缘由传到了reason参数中。即使是有错误的代码也不会报错了,这与咱们的try/catch语句有相同的功能。
all的用法
Promise的all方法提供了并行执行异步操做的能力,而且在全部异步操做执行完后才执行回调。咱们仍旧使用上面定义好的runAsync一、runAsync二、runAsync3这三个函数,看下面的例子:

Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操做的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操做返回的数据哪里去了呢?都在then里面呢,all会把全部异步操做的结果放进一个数组中传给then,就是上面的results。因此上面代码的输出结果就是:

有了all,你就能够并行执行多个异步操做,而且在一个回调中处理全部的返回数据,是否是很酷?有一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载须要用到的各类资源如图片、flash以及各类静态文件。全部的都加载完后,咱们再进行页面的初始化。

race的用法
all方法的效果其实是「谁跑的慢,以谁为准执行回调」,那么相对的就有另外一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词原本就是赛跑的意思。race的用法与all同样,咱们把上面runAsync1的延时改成1秒来看一下:

Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

这三个异步操做一样是并行执行的。结果你应该能够猜到,1秒后runAsync1已经执行完了,此时then里面的就执行了。结果是这样的:

你猜对了吗?不彻底,是吧。在then里面的回调开始执行时,runAsync2()和runAsync3()并无中止,仍旧再执行。因而再过1秒后,输出了他们结束的标志。

这个race有什么用呢?使用场景仍是不少的,好比咱们能够用race给某个异步请求设置超时时间,而且在超时后执行相应的操做,代码以下:

//请求某个图片资源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = 'xxxxxx';
    });
    return p;
}

//延时函数,用于给请求计时
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});

requestImg函数会异步请求一张图片,我把地址写为"xxxxxx",因此确定是没法成功请求到的。timeout函数是一个延时5秒的异步操做。咱们把这两个返回Promise对象的函数放进race,因而他俩就会赛跑,若是5秒以内图片请求成功了,那么遍进入then方法,执行正常的流程。若是5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。运行结果以下:
image

==几种常见的http状态码==

2XX----成功

  • 200 ok 成功
  • 204 No Content 请求处理成功,可是没有资源返回
  • 206 Partial Content 对资源某一部分的请求

3XX----重定向

  • 301 永久重定向
  • 302 临时重定向
  • 304 资源已找到,可是没有符合条件的请求

4XX----客户端错误

  • 400 请求报文中存在语法错误
  • 401 须要http认证
  • 403 对请求资源的访问被服务器给拒绝了
  • 404 页面找不到了

5XX----服务器错误

  • 500 内部资源出故障了
  • 503 服务器正在超负载的工做或者停机维护

http方法

  • get----获取资源
  • post----修改数据
  • put----传输文件
  • head----获取报文的头部
  • delete----删除文件
  • options----询问支持的方法
  • trace----追踪路径

html&css篇

==语义化的html==

1、什么是语义化的HTML?

语义化的HTML就是正确的标签作正确的事情,可以便于开发者阅读和写出更优雅的代码的同时让网络爬虫很好地解析。

2、为何要作到语义化?

一、有利于SEO,有利于搜索引擎爬虫更好的理解咱们的网页,从而获取更多的有效信息,提高网页的权重。

二、在没有CSS的时候可以清晰的看出网页的结构,加强可读性。

三、便于团队开发和维护,语义化的HTML可让开发者更容易的看明白,从而提升团队的效率和协调能力。

四、支持多终端设备的浏览器渲染。

3、语义化HTML该怎么作呢?

在作前端开发的时候要记住:HTML 告诉咱们一块内容是什么(或其意义),而不是它长的什么样子,它的样子应该由CSS来决定。(结构与样式分离!)

写语义化的 HTML 结构其实很简单,首先掌握 HTML 中各个标签的语义,在看到内容的时候想一想用什么标签能更好的描述它,是什么就用什么标签。

<h1>~<h6> ,做为标题使用,而且依据重要性递减,<h1> 是最高的等级。

<p>段落标记,知道了 <p>
做为段落,你就不会再使用 <br />
来换行了,并且不须要 <br />
来区分段落与段落。<p>
中的文字会自动换行,并且换行的效果优于 <br />。
段落与段落之间的空隙也能够利用 CSS 来控制,很容易并且清晰的区分出段落与段落。

<ul>、<ol>、<li>,<ul> 无序列表,这个被你们普遍的使用,<ol>
有序列表也挺经常使用。在 web 标准化过程当中,<ul>
还被更多的用于导航条,原本导航条就是个列表,这样作是彻底正确的,
并且当你的浏览器不支持 CSS 的时候,导航连接仍然很好使,只是美观方面差了一点而已。

<dl>、<dt>、<dd>,<dl> 就是“定义列表”。好比说词典里面的词的解释、定义就能够用这种列表。

<em>、<strong>,<em> 是用做强调,<strong> 是用做重点强调。

<q>也不只仅只是为文字增长双引号,而是表明这些文字是引用来的。

<table>、<td>、<th>、<caption>, (X)HTML中的表格再也不是用来布局。

补充:网络爬虫,SEO等概念

SEO:Search Engine Optimization

——搜索引擎优化,这是一种利用搜索引擎的搜索规则,采起优化策略或程序,提升网站在搜索结果中的排名。

网络爬虫:

又称网络蜘蛛、网络机器人,是一种搜索引擎用于自动抓取网页资源的程序或者说叫机器人。从某一个网址为起点,去访问,而后把网页存回到数据库中,如此不断循环,通常认为搜索引擎爬虫都是靠连接爬行的,因此管他叫爬虫。这个只有开发搜索引擎才会用到。对于网站而言,只要有连接指向咱们的网页,爬虫就会自动提取咱们的网页。

h5新增标签

image

==position==

1.position中 relative和absolute,fix的区别

  • fixed 属性会固定不动,不会随着屏幕的滚动滚动
  • absolute:
    舒适提示的《CSS完全研究》对绝对定位描述以下:一、使用绝对定位的盒子以它的“最近”的一个“已定位”(position属性被设置,而且被设置为不是static的任意一种)的“祖先元素”为基准进行偏移。若是没有已经定位的祖先元素,那么会以浏览器窗口为基准进行定位。二、绝对定位的框从标准流中脱离,这意味着它们对其后的兄弟盒子的定位没有影响,其余的盒子就好像这个盒子不存在同样。

3.生成绝对定位的元素,相对于 static 定位之外的第一个父元素进行定位。元素的位置经过 "left", "top", "right" 以及 "bottom" 属性进行规定。4.子元素:position:absolute也能够==相对于父元素==position:absolute

  • relative:这个是相对于,他不加定位以前的位置,定位以后,他原来的位置不清空,其余元素不能占用,会影响其余盒子的位置。

==css垂直水平居中(不知宽高)==

方法一

  • 思路:显示设置父元素为:table,子元素为:cell-table,这样就能够使用vertical-align: center,实现水平居中
  • 优势:父元素(parent)能够动态的改变高度(table元素的特性)
  • 缺点:IE8如下不支持
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>未知宽高元素水平垂直居中</title>
</head>
<style>

.parent1{
    display: table;
    height:300px;
    width: 300px;
    background-color: #FD0C70;
}
.parent1 .child{
    display: table-cell;
    vertical-align: middle;
    text-align: center;
    color: #fff;
    font-size: 16px;
}

</style>
<body>
    <div class="parent1">
        <div class="child">hello world-1</div>
    </div>
</body>
</html>

方法二:

  • 思路:使用一个空标签span设置他的vertical-align基准线为中间,而且让他为inline-block,宽度为0
  • 缺点:多了一个没用的空标签,display:inline-blockIE 6 7是不支持的(添加上:_zoom1;*display:inline)。
  • 固然也能够使用伪元素来代替span标签,不过IE支持也很差,但这是后话了
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>未知宽高元素水平垂直居中</title>
</head>
<style>
.parent2{
    height:300px;
    width: 300px;
    text-align: center;
    background: #FD0C70;
}
.parent2 span{
    display: inline-block;;
    width: 0;
    height: 100%;
    vertical-align: middle;
    zoom: 1;/*BFC*/
    *display: inline;
}
.parent2 .child{
    display: inline-block;
    color: #fff;
    zoom: 1;/*BFC*/
    *display: inline;
}

</style>
<body>
    <div class="parent1">
        <div class="child">hello world-1</div>
    </div>

    <div class="parent2">
        <span></span>
        <div class="child">hello world-2</div>
    </div>
</body>
</html>

方法三

  • 思路:子元素绝对定位,距离顶部 50%,左边50%,而后使用css3 transform:translate(-50%; -50%)
  • 优势:高大上,能够在webkit内核的浏览器中使用
  • 缺点:不支持IE9如下不支持transform属性
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>未知宽高元素水平垂直居中</title>
</head>
<style>
.parent3{
    position: relative;
    height:300px;
    width: 300px;
    background: #FD0C70;
}
.parent3 .child{
    position: absolute;
    top: 50%;
    left: 50%;
    color: #fff;
    transform: translate(-50%, -50%);
}
</style>
<body>
<div class="parent3">
        <div class="child">hello world-3</div>
    </div>
</body>
</html>

方法四:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>未知宽高元素水平垂直居中</title>
</head>
<style>
.parent4{
    display: flex;
    justify-content: center;
    align-items: center;
    width: 300px;
    height:300px;
    background: #FD0C70;
}
.parent4 .child{
    color:#fff;
}
</style>
<body>div> <div class="parent4">
        <div class="child">hello world-4</div>
    </div>
</body>
</html>

==margin折叠==

margin 折叠--margin值合并

代码

<!DOCTYPE html>
 <html>
 <head lang="en">
       <meta charset="UTF-8">
      <title>测试</title>
    </head>
 <style>
        *{
            margin: 0;
            padding: 0;
        }
        #father{
            width: 2000px;
            height: 2000px;
            background: #0016d9;
            overflow: hidden;
                   }
        #first-child{
            margin-top: 20px;
            background: chocolate;
            width: 60px;
            height: 60px;
        }
        #second-child{
            background: chartreuse;
            width: 60px;
            height: 60px;
            margin-bottom: 20px;
        }
        #three-child{
            margin-top:40px;
            background: fuchsia;
            width: 60px;
            height: 60px;
            display: inline-block;
        }

     </style>
<body>
    <div id="father">
        <div id="first-child">box1</div>
        <div id="second-child">box2</div>
        <div id="three-child">box3</div>
</div>
 </body>
 </html>
  1. 子元素的父元素的间距问题:
  • 在父层div加上:overflow:hidden;
  • 把margin-top外边距改为padding-top内边距;
  • 父元素产生边距重叠的边有不为 0 的 padding 或宽度不为 0 且 style 不为 none 的 border

父层div加: padding-top: 1px,或者 border-top:1px ;

  • 设置父元素或子元素浮动(left/right)
  • 设置父元素dispaly:inline-block或者display:table-cell;
  • 给父元素加上绝对定位
  1. 相邻元素之间的margin
  • 给最后面的元素加上浮动(left/right)
  • 给最后一个元素加上display:inline-block;可是IE6和IE7下不彻底支持display:inline-block,因此要加上*display:inline;zoom:1便可解决IE六、7的bug;

==清除浮动==

一、父级div定义伪类:after和zoom

复制代码

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red;}
   
   .div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   
   .right{float:right;width:30%;height:80px;background:#DDD}
   
   /*清除浮动代码*/
   .clearfloat:after{display:block;clear:both;content:"";visibility:hidden;height:0}
   
   .clearfloat{zoom:1}
 </style> 
   
   
<div class="div1 clearfloat"> 
    <div class="left">Left</div> 
    <div class="right">Right</div> 
</div>

<div class="div2">
   div2
</div>
  • 原理:IE8以上和非IE浏览器才支持:after,原理和方法2有点相似,zoom(IE转有属性)可解决ie6,ie7浮动问题
  • 优势:浏览器支持好,不容易出现怪问题(目前:大型网站都有使用,如:腾迅,网易,新浪等等)
  • 缺点:代码多,很多初学者不理解原理,要两句代码结合使用,才能让主流浏览器都支持
  • 建议:推荐使用,建议定义公共类,以减小CSS代码
  • 评分:★★★★☆

2.在结尾处添加空div标签clear:both

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red}
   .div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   
   /*清除浮动代码*/
   .clearfloat{clear:both}
   </style> 
<div class="div1"> 
<div class="left">Left</div> 
<div class="right">Right</div>
<div class="clearfloat"></div>
</div>
<div class="div2">
   div2
   </div>
  • 原理:添加一个空div,利用css提升的clear:both清除浮动,让父级div能自动获取到高度
  • 优势:简单,代码少,浏览器支持好,不容易出现怪问题
  • 缺点:很多初学者不理解原理;若是页面浮动布局多,就要增长不少空div,让人感受很不爽
  • 建议:不推荐使用,但此方法是之前主要使用的一种清除浮动方法
  • 评分:★★★☆☆

3.父级div定义height

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red;/*解决代码*/height:200px;}
   .div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   </style> 
<div class="div1"> 
<div class="left">Left</div> 
<div class="right">Right</div> 
</div>
<div class="div2">
   div2
   </div>
  • 原理:父级div手动定义height,就解决了父级div没法自动获取到高度的问题
  • 优势:简单,代码少,容易掌握
  • 缺点:只适合高度固定的布局,要给出精确的高度,若是高度和父级div不同时,会产生问题
  • 建议:不推荐使用,只建议高度固定的布局时使用
  • 评分:★★☆☆☆

4.父级div定义overflow:hidden

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red;/*解决代码*/width:98%;overflow:hidden}
   .div2{background:#800080;border:1px solid red;height:100px;margin-top:10px;width:98%}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   </style> 
<div class="div1"> 
<div class="left">Left</div> 
<div class="right">Right</div>
</div>
<div class="div2">
   div2
   </div>
  • 原理:必须定义width或zoom:1,同时不能定义height,使用overflow:hidden时,浏览器会自动检查浮动区域的高度
  • 优势:简单,代码少,浏览器支持好
  • 缺点:不能和position配合使用,由于超出的尺寸的会被隐藏
  • 建议:只推荐没有使用position或对overflow:hidden理解比较深的朋友使用
  • 评分:★★★☆☆

5.父级div定义overflow:auto

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red;/*解决代码*/width:98%;overflow:auto}
   .div2{background:#800080;border:1px solid red;height:100px;margin-top:10px;width:98%}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   </style> 
<div class="div1"> 
<div class="left">Left</div> 
<div class="right">Right</div>
</div>
<div class="div2">
   div2
   </div>
  • 原理:必须定义width或zoom:1,同时不能定义height,使用overflow:auto时,浏览器会自动检查浮动区域的高度
  • 优势:简单,代码少,浏览器支持好
  • 缺点:内部宽高超过父级div时,会出现滚动条。
  • 建议:不推荐使用,若是你须要出现滚动条或者确保你的代码不会出现滚动条就使用吧。
  • 评分:★★☆☆☆

6.父级div也一块儿浮动

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red;/*解决代码*/width:98%;margin-bottom:10px;float:left}
   .div2{background:#800080;border:1px solid red;height:100px;width:98%;/*解决代码*/clear:both}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   </style> 
<div class="div1"> 
<div class="left">Left</div> 
<div class="right">Right</div>
</div>
<div class="div2">
   div2
   </div>
  • 原理:全部代码一块儿浮动,就变成了一个总体
  • 优势:没有优势
  • 缺点:会产生新的浮动问题。
  • 建议:不推荐使用,只做了解。
  • 评分:★☆☆☆☆

7.父级div定义display:table

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red;/*解决代码*/width:98%;display:table;margin-bottom:10px;}
   .div2{background:#800080;border:1px solid red;height:100px;width:98%;}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   </style> 
<div class="div1"> 
<div class="left">Left</div> 
<div class="right">Right</div>
</div>
<div class="div2">
   div2
   </div>
  • 原理:将div属性变成表格
  • 优势:没有优势
  • 缺点:会产生新的未知问题
  • 建议:不推荐使用,只做了解
  • 评分:★☆☆☆☆

八、结尾处加br标签clear:both

<style type="text/css"> 
   .div1{background:#000080;border:1px solid red;margin-bottom:10px;zoom:1}
   .div2{background:#800080;border:1px solid red;height:100px}
   
   .left{float:left;width:20%;height:200px;background:#DDD}
   .right{float:right;width:30%;height:80px;background:#DDD}
   
   .clearfloat{clear:both}
   </style> 
<div class="div1"> 
<div class="left">Left</div> 
<div class="right">Right</div>
<br class="clearfloat" />
</div>
<div class="div2">
   div2
   </div>
  • 原理:父级div定义zoom:1来解决IE浮动问题,结尾处加br标签clear:both
  • 建议:不推荐使用,只做了解
  • 评分:★☆☆☆☆

==box-sizing==

box-sizing:content-box

box-sizing:content-box状况下,元素的宽度=width+pading+border;
解释:box-sizing:content-box,至关于你从网上买东西,content-box为你买的实际要用的东西,假设为A。
快递员配送快递的时候,实际上你收到的快递是带有包装的A。

类比一下,content-box是A,box-sizing是收快递的你,赋值是快递员配送,
最后你手里收到的东西就是A+包装盒,也就是content+border+padding;

/ width 和 height 属性包括内容,内边距和边框,但不包括外边距 /

box-sizing:border-box

状况下,元素的宽度=width,pading,border都包含在width里面
解释:box-sizing:border-box;至关于你从网上买东西,border-box是带有包装的你买的东西,假设为B。
快递员配送快递的时候,实际上你收到的快递就是B。

类比一下,border-box是B,box-sizing是收快递的你,赋值是快递员配送,
最后你手里收到的东西就是B;

==列举一些常见的块级元素和行内元素==

  • 行内元素有:title lable span br a em b i strong
  • 块级元素有:body form select textarea h1-h6 table button hr p ol ul dl div
  • 行内块元素常见的有: img input td

==CSS的优先级【class,id,内联,!important】==

!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性

类选择器和属性选择器优先级相同,谁在后面谁的优先级较高

注意:通用选择器(*),子选择器(>)和相邻同胞选择器(+),他们的权值是0,因此两个通配符选择器和一个通配符选择器的权值是相同的
  1. 内联样式表的权值为 1000,就是在元素内写style
  2. ID 选择器的权值为 100
  3. Class 类选择器的权值为 10
  4. HTML 标签选择器的权值为 1

==对inline元素设置padding、margin有效吗?==

  • inline元素设置width和height无效
  • 设置margin-left、margin-right、padding-left、padding-right有效
  • 设置margin-top、margin-bottom、padding-top、padding-bottom无效

==line-heghit==

line-height的继承【带单位和不带单位】

line-height是能够继承的,因此子元素就能够不用重复定义line-height了。咱们通常也会在后面带上单位(如:line-height:22px; 或是line-height:1.4em;),但line-height会给人带来麻烦的地方也就是这个继承和后面加的单位。

【Css高级应用】line-height带单位与不带单位的区别

有的时候,咱们为了实现单行文字的垂直居中,会给line-height一个和height相同的固定的值;有的时候,咱们为了调整特定的某段文字 的行间距,一般会考虑使用百分比或者相对尺寸的em。
或许是习惯,因而咱们都习惯了line-height是要有单位的。
这些状况下,咱们都不须要考虑 line-height的继承,也不会发现任何问题,固然后咱们在使用到line-height继承的时候,就会发现问题的所在。
例以下面的代码:

一、样式

<style type="text/css">
 .test{line-height:1.4em; font-size:12px;}
 span{font-size:30px; font-weight:bold;}
 </style>

二、HTML结构

<div class="test">
    <span> 白培铭先生于1960年出生于中国台湾。<br/>
            毕业于中国台湾省清华大学核物理系,<br/>
    </span>
    以后留学于美国加州大学伯克利分校和密西根大学,得到双硕士学位。<br/>
    在工做以后,凭着对营销领域的浓厚兴趣,他又考入密执安大学深造<br/>
</div>

看过例子后,你会发现,只要有单位的line-height继承,都发生了重叠的现象。那到底这是什么缘由致使的呢?

  • 若是line-height属性值有单位,那么继承的值则是换算后的一个具体的px级别的值;
  • 而若是属性值没有单位,则浏览器会直接继承这个 “因子(数值)”,而非计算后的具体值,此时它的line-height会根据本==身的font-size==值从新计算获得新的line-height 值。

==rem和em的区别?==

css中单位em和rem的区别
在css中单位长度用的最多的是px、em、rem,这三个的区别是:

  •   px是固定的像素,一旦设置了就没法由于适应页面大小而改变。
  •   em和rem相对于px更具备灵活性,他们是相对长度单位,意思是长度不是定死了的,更适用于响应式布局。
  • 对于em和rem的区别一句话归纳:em相对于父元素,rem相对于根元素。
  • rem中的r意思是root(根源),这也就不难理解了。

em

  • 子元素字体大小的em是相对于父元素字体大小
  • 元素的width/height/padding/margin用em的话是相对于该元素的font-size

rem

  • rem是所有的长度都相对于根元素,根元素是谁?<html>元素。一般作法是给html元素设置一个字体大小,而后其余元素的长度单位就为rem。

==元素position有哪几种【static、relative、absolute、fixed】==

  • static:没有定位,在正常的流中
  • relative:相对于正常位置
  • absolute:至关于第一个父元素进行定位
  • fixed:相对于浏览器的窗口进行定位

==BFC==

块级格式上下文Block Formatting Context(简称BFC ),这是Web页面的一种布局方式,通俗点说,也是指页面上一个渲染区域,里面的元素按文档流中的顺序垂直排列,而且发生垂直方向上的margin折叠,同时这个区域内的元素布局不会对外面的元素有任何影响,反过来,也是同样。

当元素知足一下任何一个条件是都会产生一个BFC:

  • float属性取值不是“none”
  • overflow属性取值不是“visible”
  • display的值为 “table-cell”, “table-caption”, or “inline-block”中的任何一个
  • position的值不为 “static” 或 “relative”中的任何一个

相关文献

相关文章
相关标签/搜索