JavaScript 三座大山

来源:互联网 发布:java使用md5加密解密 编辑:程序博客网 时间:2024/05/16 14:58

原型与原型链及this

声明:下面说的对象是指 object Array Function 除 null 以外
1 任何对象 都可以做自由添加属性

var obj = {a:10, b:1}obj.qq = "123456";//Object {a: 10, b: 1, qq: "123456"}

2 在JavaScript中任何对象都会有__proto__ 属性 (隐式原型)

3 在JavaScript中函数对象 都会有prototype属性 (显式原型)

4 任意对象的__proto__ (隐式原型)指向 其构造函数的prototype(显式原型)

//安装规范 构造函数名 采用大驼峰命名方式 var Foo = function () {    this.name = "long";    this.age = "10";}var foo = new Foo();foo.__proto__ === Foo.prototype//true

5 任何一个函数调用其属性时,如果自己没有对应的属性就回去父类上找,通过原型链一步步向上查找,直至null

var Foo = function () {    this.name = "long";    this.age = "10";}Foo.afert = function () {console.log(1);}var foo = new Foo();foo.__proto__ === Foo.prototype//trueFoo.__proto__ === Function.prototype//truefoo.toString();//有toString方法,因为Function 有。foo.name;//用为其父类有//"long"foo.prototype//undefinedfoo.prototype = {ele: "element", clazz: "class"}//Object {ele: "element", clazz: "class"}foo.prototype.clazz//"class"foo.prototype.hasOwnProperty("toString")//falsefoo.prototype.hasOwnProperty("ele") //可以通过hasOwnProperty方法判断某个属性是对象自己的还重父类继承过来的//truefoo.name//"long"foo.age//"10"foo.afert//undefined//prototype (显式原型上的属性是可以被 子类继承的)//Foo.xxx  xxx属性是不会被继承的

========== this 指向问题先要搞清js的 “定义”与“执行”============

this 要在执行时才能确定值,定义时无法确认("谁调指谁,没有谁调用时指window")eg:var a = {    name: "long",    fn: function () {        console.log(this.name);//就是这里有bug 在定义时也无法确定    }}a.fn();// this === aa.fn.call({name: "gong"})// this === {name: "gong"}var fn1 = a.fn;fn1(); // this === window//this 的指向作为构造函数执行     this.name .....作为对象属性执行     f.callname()作为普通函数执行     username()   this 指windowcall apply bind      可以改变this的执行  call(面量对象)  apply(数组)eg://执行构造函数function Foo (name) {    //this = {};    this.name = name;    //return this;}var f = new Foo('long');f.name;//执行对象属性var obj = {    name: 'long',    sex: 'man',    say: function() {        console.log(this.name);    }}obj.say();// this === obj//作为普通函数执行function testfn () {    console.log(this);//this === window}testfn();//call apply bind/*全局中:在执行前会确认一遍, 变量定义 函数声明.函数中:在执行前会确认一遍,变量定义 函数声明 this argument*/function fn1 (name) {    console.log(name);    console.log(this);    console.log(arguments);}fn1.call({x:100}, 'long', '25');// this === {x:100}fn1();function fn11 (name) {    console.log(name);    console.log(this);    console.log(arguments);};fn11.apply({x:100}, ['long', '25']);// this === {x:100}fn11();function fn2 (name) {    console.log(name);    console.log(this);}fn2("long");// this === windowfunction fn3 (name) {    console.log(name);    console.log(this);}.bind({y: 200});// bind 就是用来改变this指向的fn3("long");//【.bind 必须是函数表达式  如果是函数表达式会报错】var fn4 = function (name) {    console.log(name);    console.log(this);}.bind({y: 200});// bind 就是用来改变this指向的fn4("long");// this === {y: 200}//分清 函数定义 和 函数执行注意:像第三方库 一般第三方库都使用call来改变 this 的指向。.bind 必须是函数表达式  如果是函数表达式会报错

作用域与作用域链及闭包

JavaScript作用域

  1. 无块级作用域
  2. 只有函数和全局作用域
作用域无块级作用域只有函数和全局作用域//无块级作用域if (true) {    var name = 'long';}console.log(name);////在高级语言如java 中就会报错。因为 var name = "long";是块级作用域,超出块级作用域范围的console.log(name);会报错而在JavaScript中等同与下面这样(推荐使用)var name;//先定义if (true) {    name = "long";}console.log(name);//函数作用域var a = 10000;function fn() {    var a = 1000;    console.log('fn', a);}console.log("global", a);fn();作用域链eg:var a = 10000;function fn () {    var b = 1000;    function fn1 () {        var c = 100;        function fn2 () {            var d = 10;            console.log(d);//10            console.log(c);//100   自由变量            console.log(b);//1000  自由变量            console.log(a);//10000 自由变量        }        d = 20000;        fn2();        console.log("1  "+d);    }    fn1();    console.log("2  "+d);}fn();下面两个例子需要好好思考//dome1var a = 10000;function fn () {    var b = 1000;    function fn1 () {        var c = 100;        function fn2 () {            var d = 10;            console.log(d);//10            console.log(c);//100   自由变量            console.log(b);//1000  自由变量            console.log(a);//10000 自由变量        }        fn2();        console.log("1  "+d);        d = 20000;    }    fn1();      d = 30000;    console.log("2  "+d);}fn();101001000100001  20000  //  ????  涉及到没有var 定义的变量处理情况。2  30000//dome2var a = 10000;function fn () {    var b = 1000;    function fn1 () {        var c = 100;        function fn2 () {            var d = 10;            console.log(d);//10            console.log(c);//100   自由变量            console.log(b);//1000  自由变量            console.log(a);//10000 自由变量        }        fn2();        console.log("1  "+d);        d = 20000;    }    d = 30000;    fn1();    console.log("2  "+d);}fn();101001000100001  30000  //函数fn2 里面的值受影响 ????2  20000  //函数fn2 里面的值影响 函数fn1 里面的值 ??? //demo3var a = 100;function fn() {    var b = 200;    console.log(a);//自由变量(没有找到就去"父级作用域"找)    console.log(b);}var b = 20000;fn ();注意 函数外的变量一定不会影响函数里面的变量//demo4var a = 100;function fn() {    var b = 200;    console.log(a);    console.log(b);}b = 20000;fn ();//当前作用域没有定义的变量,即"自由变量"定义   执行闭包function Fn () {    var a = 100;    //返回一个函数 (函数作为返回值)    return function () {// 一个函数的父级作用域是其定义时的作用域 不是 执行时的作用域        console.log(a);//a自由变量,父作用域寻找。  (a此时形成了闭包) (那个作用域定义了他就去那个作用找,一直往上查找)    }}// fn 得到一个函数var fn = Fn();var a = 200;//这个a与Fn函数里面的a是两码事fn();//100 // 一个函数的父级作用域是其定义时的作用域 不是 执行时的作用域。如果是看函数fn执行时的作用域那fn()==>200,显然不是,所以只有是看函数fn定义时的作用域那fn() ==> 100 才是正确的//new 才会动用到构造函数里面的thisfunction Fn () {    this.name = "123";    var a = 100;    //返回一个函数 (函数作为返回值)}// fn 得到一个函数var fn = new Fn();var a = 200;fn.name;//123闭包的使用场景1 函数作为返回值2 函数作为参数传递function Fn () {    var a = 100;    return function () {        console.log(a);    }}var fn = Fn();var a = 200;fn();//100function Fn () {    var a = 100;    return function () {        console.log(a);// a 自由变量,父作用域寻找。父作用域是在其声明定义时的作用域,而不是其执行的作用域。本demo中的执行作用域是Fn1的作用域    }}function Fn1 (fn) {    var a = 200;    fn();}var fn1 = Fn();Fn1(fn1);说一下对变量提升的理解 是执行上下文的概念,变量定义和函数声明(注意和函数表达式的区别) 都会在执行之前 全局作用域  执行前会先确定  变量定义 和 函数声明 函数作用域  执行前会先确定  变量定义 和 函数声明 this arguments创建10个<a>标签 点击的时候弹出来对应的序号//下面的是错误的,十个alert出来的都是9 var i, a; for (i = 0; i < 10; i++) {    a = document.createElement('a');    a.innerHTML = i + '<br/>';    a.addEventListener('click', function (e) {        e.preventDefault();        alert(i);//i 是自由变量 需要从父级作用域找寻i,但是此时i早就变成了10 (本父级作用域 是全局作用域)    })    document.body.appendChild(a) } var i, a; for (i = 0; i < 10; i++) {    //自执行函数,就是不用调用,只要定义完成,立即执行的函数    (function (i) {//i 函数作用域 定了就不受外面的影响了        a = document.createElement('a');        a.innerHTML = i + '<br/>';        a.addEventListener('click', function (e) {            e.preventDefault();            alert(i);//i 是函数作用的, 需要从父级作用域中寻找 (本父级作用域 是函数作用域);被使用的变量 内存回收机制是不会回收的        })        document.body.appendChild(a)    })(i)//形成10个函数 }//下面两个只是改变this的指向又不能形成闭包 var i, a; for (i = 0; i < 10; i++) {    function fna () {        a = document.createElement('a');        a.innerHTML = i + '<br/>';        a.addEventListener('click', function (e) {            e.preventDefault();            alert(i);        });        document.body.appendChild(a)    }    fna.bind({i: i});    fna(); } var i, a; for (i = 0; i < 10; i++) {    function fna () {        a = document.createElement('a');        a.innerHTML = i + '<br/>';        a.addEventListener('click', function (e) {            e.preventDefault();            alert(i);        });        document.body.appendChild(a)    }    fna.call({i: i}); }如何理解作用域链 自由变量 作用域链,即自由变量的查找 闭包的两个两个场景(函数作为返回值,函数作为参数传入)实际开发中闭包的应用  // 闭包实际应用中主要用于封装变量,收敛权限  function isFirstLoad() {    var _list = [];    return function (id) {        if (_list.indexOf(id) >= 0) {            return false;        } else {            _list.push(id);            return true;        }    }  }  //使用  var firstLoad = isFirstLoad();  firstLoad(10);//true  firstLoad(10);//false  firstLoad(20);//true  //在isFirstLoad 函数外面,根本不可能修改 _list的值

单线程与异步

1 单线线程是对多线程的,对于多线程的开发语言,有一个请求就可以开一个线程处理。那,对于单线程语言,只有通过异步调用程序。【事件 (事件循环机制), 回调】

2 异步是对同步说的, 最大区别就是同步需要等待,异步这不需要等待。

【异步】非阻塞 (function funyb () {    console.log(1);    setTimeout(function () {console.log(2)}, 3000);    console.log(3);  })()  //1  //3  //2注意: setTimeout(fun,ms);//fun 是一个函数。 比如 function(){console.log(2)}//fun 不能是一个函数执行。 比如 console.log(2) 这样setTimeout的 ms数就没有用了。而是立即执行【同步】会阻塞(function funyb () {    console.log(1);    alert(2);//这个执行完才会往下。alert 不点击确定就一直不执行console.log(3)    console.log(3);  })()

会采用异步的三种情况 可以写3个例子分别看看

  1. 在定时执行时, 如:setTimeout,setInterval
  2. 请求数据和图片、音频、视频资源时,如 ajax 、img 、audio、video …. 这些资源都要很长的等待时间
  3. 事件,因为事件只是在用户点击的时候才会触发

!!!需要等待 就异步
!!!需要等待 就异步
!!!需要等待 就异步

注意:闭包容易出现内存泄漏,就是内存空间无法被gc回收。

//10个a标签Array.prototype.slice.call(document.querySelectorAll("a")).forEach(function (item, index) {    //在内存中形成10 closest 函数。会占用内存。只是i的值不同0 -> 9    function closest (i) {        //代码.....    }.bind({i: index}) //父类还没用完 就不能清除    //这两个是一样的    //(function closest (i) {    //  //代码....        //})(index)})

经典dome

原创粉丝点击