javascript经典问题总结及代码实例(未完待续)

函数

匿名函数当即调用php

(function (){alert(1)}())

function的左圆括号是必须的正则表达式

函数的调用

1,做为函数的调用,
2,做为方法的调用
嵌套函数的this没有做用域的限制,若是做为方法调用,指调用它的对象,若是做为函数调用,this是全局对象或undefinedjson

var o={
    m:function(){
        var self=this;
        console.log(this===o)
        f()
        function f(){
            console.log(this===o)//false
            console.log(self===o)//true
        }
    }
}

3,做为构造函数的调用
建立空对象,对象的原型是构造函数的prototype,构造函数使用this引用这个新对象,那么 var a=new o.m()中的m中的this就不是o而是a数组

4,间接调用浏览器

bind apply call的区别

apply和call是对函数的调用,第一个参数是改变this的指向的对象,call第二个以上传的是每一个参数,apply第二个参数是数组,将要传递的参数写在数组里
两个方法使得任何函数能够做为任意对象的方法来调用缓存

bind的第一个参数是用来改变原函数this的指向,返回一个新函数,不进行调用服务器

function f(y){return this.x+y}
var o={x:1}
var g=f.bind(o)
g(2)//3

柯里化闭包

var sum=function(x,y){return x+y}
var suc=sum.bind(null,1)
suc(2)//3

关于this

函数里的this,谁调用它就是谁,做为谁的构造函数就是谁,经过apply call能够改变app

什么是做用域

js中每一个函数是一个做用域,在函数中声明的变量和函数的参数在整个函数体包括其嵌套函数都是可见的dom

var scope="gl"
function ch(){
    var scope="cb"
    function ne(){
        var scope='ne'
        return scope
    }
    return ne()
}
ch()//ne


//
function test(o){
    var i=0;
    if(typeof o=='object'){
        var j=0;
        for(var k=0;k<10;k++){
            console.log(k)
        }
        console.log(k)
    }
    console.log(j)
}
test({a:7})//i k j都是在该函数做用域内有定义的

声明提早,只要变量在函数内定义了,这个变量在该做用域的任意地方均可用,甚至在变量以前的位置

//
var scope='glo'
function f(){
    console.log(scope)
    var scope='local'
    console.log(scope)
}
f()//undefined local

做用域链
变量层层向上向外的做用域寻找(函数的参数属于函数的局部变量)

函数递归

函数本身调用本身

什么是闭包

闭包(closure)是Javascript语言的一个难点,也是它的特点,不少高级应用都要依靠闭包实现。

下面就是个人学习笔记,对于Javascript初学者应该是颇有用的。

1、变量的做用域

要理解闭包,首先必须理解Javascript特殊的变量做用域。

变量的做用域无非就是两种:全局变量和局部变量。

Javascript语言的特殊之处,就在于函数内部能够直接读取全局变量。

  

var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999

另外一方面,在函数外部天然没法读取函数内的局部变量。

function f1(){
    var n=999;
  }

  alert(n); // error

这里有一个地方须要注意,函数内部声明变量的时候,必定要使用var命令。若是不用的话,你实际上声明了一个全局变量!

 

function f1(){
    n=999;
  }

  f1();

  alert(n); // 999

2、如何从外部读取局部变量?

出于种种缘由,咱们有时候须要获得函数内的局部变量。可是,前面已经说过了,正常状况下,这是办不到的,只有经过变通方法才能实现。

那就是在函数的内部,再定义一个函数。

  

function f1(){

    var n=999;

    function f2(){
      alert(n); // 999
    }

  }

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的全部局部变量,对f2都是可见的。可是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式做用域"结构(chain scope),子对象会一级一级地向上寻找全部父对象的变量。因此,父对象的全部变量,对子对象都是可见的,反之则不成立。

既然f2能够读取f1中的局部变量,那么只要把f2做为返回值,咱们不就能够在f1外部读取它的内部变量了吗!

  

function f1(){

    var n=999;

    function f2(){
      alert(n); 
    }

    return f2;

  }

  var result=f1();

  result(); // 999

3、闭包的概念

上一节代码中的f2函数,就是闭包。

各类专业文献上的"闭包"(closure)定义很是抽象,很难看懂。个人理解是,闭包就是可以读取其余函数内部变量的函数。

因为在Javascript语言中,只有函数内部的子函数才能读取局部变量,所以能够把闭包简单理解成"定义在一个函数内部的函数"。

因此,在本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁。

4、闭包的用途

闭包能够用在许多地方。它的最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。

  

function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证实了,函数f1中的局部变量n一直保存在内存中,并无在f1调用后被自动清除。

为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另外一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,所以nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数自己也是一个闭包,因此nAdd至关因而一个setter,能够在函数外部对函数内部的局部变量进行操做。

5、使用闭包的注意点

1)因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,因此不能滥用闭包,不然会形成网页的性能问题,在IE中可能致使内存泄露。解决方法是,在退出函数以前,将不使用的局部变量所有删除。

2)闭包会在父函数外部,改变父函数内部变量的值。因此,若是你把父函数看成对象(object)使用,把闭包看成它的公用方法(Public Method),把内部变量看成它的私有属性(private value),这时必定要当心,不要随便改变父函数内部变量的值。

6、思考题

若是你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。

代码片断一。

  

var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      return function(){
        return this.name;
      };

    }

  };

  alert(object.getNameFunc()());

代码片断二。

  

var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };

    }

  };

  alert(object.getNameFunc()());
function cr(){
        var n=0;
        return {
            count:function(){return n++},
            reset:function(){n=0}
        }
    }
    var c=cr(),d=cr()
    c.count()//0
    d.count()//0
    c.reset()
    c.count()//0
    d.count()//1

每次函数调用时,造成新的对象来保存局部变量。

垃圾回收
函数内的变量,在函数调用以后会被从内存删除,但若是函数内的嵌套函数被做为返回值,被某个变量引用,那么嵌套函数里的变量将不会被内存回收

对象

var y=x;若是x指向一个对象的引用,那么y也是,而非这个对象的复本,经过y修改对象,也会对x形成影响
建立对象的三种方式
1,直接量 var obj={a:1}
2,new 构造函数
3,Object.create(对象)

原型链
除了Object.prototype和null,任何对象都继承自另一个对象,即原型,往上追溯,造成原型链(家谱)
继承的话,不会改变原型的属性值

function inherit(p){
    if(p==null) throw TypeError()
    if(Object.create) return Object.create(p)
    var t=typeof p
    if(t!=='object' && t!=='function') throw TypeError()
    function f(){}
    f.prototype=p
    return new f()
}
var u={r:1}
var c=inherit(u)
c.x=1;c.y=1;
c.r=3
console.log(u.r)//1

删除属性
delete删除可配置性为true的自有属性,不能删除继承属性

检测对象是否有属性的四个方法
1,in

var o={x:1}
'x' in o //true
'toString' in o//true

2,hasOwnProperty() 检测自有属性,对于继承属性返回false
3,propertyIsEnumerable()检测自有属性且该属性可枚举
4,o.x!==undefined

枚举属性
for in
Object.keys() 自有可枚举的属性
Object.getOwnPropertyNames() 自有属性不只是可枚举的属性

getter&setter

var p={
    x:1.0,
    y:1.0,
    get r(){ return this.x*this.y},
    set r(v){//忽略set的返回值
      if(v>0){
          r=90
      }
    },
    get t(){
        return 100
    }
}

属性的4个特性
Object.getOwnPropertyDescriptor({x:1},'x')
//{value:1,writable:true,enumerable:true,configurable:true}
修改4个特性
var o={}
Object.defineProperty(o,'x',{value:1,writable:true,enumerable:false,configurable:true})

var p=Object.defineProperties({},{x:{value:1},y:{writable:true}})

可配置的优先级最高

clipboard.png

clipboard.png

对象的属性
原型属性
Object.getPrototypeOf()查询原型
isPrototypeOf()是不是它的原型(和instanceof类型,构成判断是不是对象的方法)

类属性
Object.prototype.toString.call(obj).slice(8,-1)

可扩展性

事件

事件冒泡:由最具体的元素(嵌套层次最深的)接收,而后逐级向上传到不具体的节点
事件捕获:过程相反,
DOM2级事件流:事件捕获=》处于目标=》事件冒泡

事件的写法:
1,行内元素
注意some和行内在同一做用域里

<div id="dom" onclick="alert('de')">点击我</div>
<div onclick="some()">点击ta</div>
<script>
function some(){
    alert('ta')
}
</script>

2,DOM0级事件
onclick=function(){}//添加
onclick=null//移除

3,DOM2级事件
addEventListener('click',fn,true捕获阶段调用|false冒泡阶段)
大可能是是冒泡阶段
removeEventListener移除的函数必须等于添加的函数,这时经过单独函数定义实现

4,事件兼容写法

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] = null;
                    }
                },
                getEvent: function(event){//获取事件对象
                    return event ? event : window.event;
                },
                getTarget: function(event){
                    return event.target || event.srcElement;
                },
                preventDefault: function(event){//阻止默认事件
                    if (event.preventDefault){
                        event.preventDefault();
                    } else {
                        event.returnValue = false;
                    }
                },
                stopPropagation: function(event){//阻止冒泡
                    if (event.stopPropagation){
                        event.stopPropagation();
                    } else {
                        event.cancelBubble = true;
                    }
                }
            };

理解事件函数里的this target currentTarget
点击页面的#mybtn按钮

document.body.onclick=function(event){
  alert(event.currentTarget===document.body)///true
  alert(this===document.body)//true
  alert(event.target===document.getElementById('mybtn'))//true
}

currentTarget始终和对象this相同,target则是事件的实际目标

js进程

js是单线程的,除了主js执行进程外,还有代码队列,随着时间,代码按照顺序添加到队列,主进程执行完后,是空闲状态,后面的进程开始执行。
定时器例子
假设,某个onclick 事件处理程序使用setInterval()设置了一个200ms 间隔
的重复定时器。若是事件处理程序花了300ms 多一点的时间完成,同时定时器代码也花了差很少的时间,
就会同时出现跳过间隔且连续运行定时器代码的状况

clipboard.png

这个例子中的第1 个定时器是在205ms 处添加到队列中的,可是直到过了300ms 处才可以执行。当
执行这个定时器代码时,在405ms 处又给队列添加了另一个副本。在下一个间隔,即605ms 处,第一
个定时器代码仍在运行,同时在队列中已经有了一个定时器代码的实例。结果是,在这个时间点上的定
时器代码不会被添加到队列中。结果在5ms 处添加的定时器代码结束以后,405ms 处添加的定时器代码
就马上执行。
为了不setInterval()的重复定时器的这2个缺点,你能够用以下模式使用链式setTimeout()
调用。

setTimeout(function(){
//处理中
setTimeout(arguments.callee, interval);
}, interval);
setTimeout(function(){
var div = document.getElementById("myDiv");
left = parseInt(div.style.left) + 5;
div.style.left = left + "px";
if (left < 200){setTimeout(arguments.callee, 50);
}
}, 50);

http

let xhr=new XMLHttpRequest();
xhr.onreadystatechange = function(){
  if (xhr.readyState == 4){//已接收到所有的相应数据
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){//304表示请求的资源没被改,直接用浏览器的缓存
        alert(xhr.responseText);
    } else {
        alert("Request was unsuccessful: " + xhr.status);
    }
  }
};
xhr.open("get", "example.txt", true);//是否异步 true异步 false同步
xhr.send(null);
``

默认状况下,在发送XHR 请求的同时,还会发送下列头部信息。
 Accept:浏览器可以处理的内容类型。
 Accept-Charset:浏览器可以显示的字符集。
 Accept-Encoding:浏览器可以处理的压缩编码。
 Accept-Language:浏览器当前设置的语言。
 Connection:浏览器与服务器之间链接的类型。
 Cookie:当前页面设置的任何Cookie。
 Host:发出请求的页面所在的域 。
 Referer:发出请求的页面的URI。注意,HTTP 规范将这个头部字段拼写错了,而为保证与规
范一致,也只能将错就错了。(这个英文单词的正确拼法应该是referrer。)
 User-Agent:浏览器的用户代理字符串。
虽然不一样浏览器实际发送的头部信息会有所不一样,但以上列出的基本上是全部浏览器都会发送的。

常常设置的请求头是

xhr.setRequestHeader('Content-Type', 'application/json')
//设置header的须要放在open以后 请求参数为JSON.stringify({})

xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
//若是请求参数需序列化为查询字符串
请求参数序列化的方法
1,使用new FormData()的方便之处体如今没必要明确地在XHR 对象上设置请求头部。XHR 对象可以识别传
入的数据类型是FormData 的实例,并配置适当的头部信息。
2,qs.stringify(json)
3,表单序列化serialize(document.getElementById("user-info"))
4,直接传'a=b&c=d'
clipboard.png

文件postexample.php 就能够经过$_POST 取得提交的数据了:

<?php
header("Content-Type: text/plain");
echo <<<EOF
Name: {$_POST[‘user-name’]}
Email: {$_POST[‘user-email’]}
EOF;
?>

若是不设置Content-Type 头部信息,那么发送给服务器的数据就不会出如今$_POST 超级全局变
量中。这时候,要访问一样的数据,就必须借助$HTTP_RAW_POST_DATA。

数据类型

5种
undefined、null、boolean、number、string、object。
typeof判断返回值
"undefined"——若是这个值未定义;
 "boolean"——若是这个值是布尔值;
 "string"——若是这个值是字符串;
 "number"——若是这个值是数值;
 "object"——若是这个值是对象或null;
 "function"——若是这个值是函数。

alert(null == undefined); //true

数据类型 转换为true的值 转换为false的值
Boolean true false
String 任何非空字符串 ""(空字符串)
Number 任何非零数字值(包括无穷大) 0和NaN
Object 任何对象 null
Undefined n/a undefined

NaN与任何数值都不相等,包括自己
NaN==NaN //false

Number()函数的转换规则以下。
 若是是Boolean 值,true 和false 将分别被转换为1 和0。
 若是是数字值,只是简单的传入和返回。
 若是是null 值,返回0。
 若是是undefined,返回NaN。
 若是是字符串,遵循下列规则:
 若是字符串中只包含数字(包括前面带正号或负号的状况),则将其转换为十进制数值,即"1"
会变成1,"123"会变成123,而"011"会变成11(注意:前导的零被忽略了);
 若是字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(一样,也会忽
略前导零);
 若是字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整
数值;
 若是字符串是空的(不包含任何字符),则将其转换为0;
 若是字符串中包含除上述格式以外的字符,则将其转换为NaN。
 若是是对象,则调用对象的valueOf()方法,而后依照前面的规则转换返回的值。若是转换
的结果是NaN,则调用对象的toString()方法,而后再次依照前面的规则转换返回的字符
串值。
根据这么多的规则使用Number()把各类数据类型转换为数值确实有点复杂。下面仍是给出几个具
体的例子吧。
var num1 = Number("Hello world!"); //NaN
var num2 = Number(""); //0
var num3 = Number("000011"); //11
var num4 = Number(true); //1

parseInt()
它会忽略字
符串前面的空格,直至找到第一个非空格字符。若是第一个字符不是数字字符或者负号,parseInt()
就会返回NaN;也就是说,用parseInt()转换空字符串会返回NaN(Number()对空字符返回0)。如
果第一个字符是数字字符,parseInt()会继续解析第二个字符,直到解析完全部后续字符或者遇到了
一个非数字字符。例如,"1234blue"会被转换为1234,由于"blue"会被彻底忽略。相似地,"22.5"
会被转换为22,由于小数点并非有效的数字字符。
若是字符串中的第一个字符是数字字符,parseInt()也可以识别出各类整数格式(即前面讨论的
十进制、八进制和十六进制数)。也就是说,若是字符串以"0x"开头且后跟数字字符,就会将其看成一
个十六进制整数;若是字符串以"0"开头且后跟数字字符,则会将其看成一个八进制数来解析。

toString()
null和undefined没有该方法

数组

var a=['s','d']
a[99]='c'
a.length//100
//其余项是undefined

数组的方法
Array.isArray()// true false

sort(比较函数)方法
比较函数接收两个参数,若是第一个参数应该位于第二个以前则返回一个负数,若是两个参数相等
则返回0,若是第一个参数应该位于第二个以后则返回一个正数

concat(单个值或数组,...) 数组的副本

正则

实例属性

g
i
m
转义:模式中使用的全部元字符都必须转义 ( [ { ^ $ | ) ? * + .]}
字面量:不加字符串 转义\
构造函数:字符串形式 。全部元字符都必须双重转义 \
(字符在字符串中一般被转义为\,而在正则表达式字符串中就
会变成\\)
/w\hello\123/ "\w\\hello\\123"

var re = null,
i;
for (i=0; i < 10; i++){
re = /cat/g;
re.test("catastrophe");
}
for (i=0; i < 10; i++){
re = new RegExp("cat", "g");
re.test("catastrophe");
}

实例方法

正则.exec(字符串)
.test
构造函数形式.toString() //字面量
构造函数形式.toLocaleString() //字面量

字符串方法

.search()
.match()
.replace()

符号

clipboard.png

相关文章
相关标签/搜索