2019-01-02ES5核心学习笔记

关于函数提升
    alert(a)    1
    a();   2
    var a=3;
    function a(){
        alert(10)
    } 
    alert(a)    3
    a=6;
    a();   4

请写出弹出值,并解释为什么?
由于函数提升,整个式子会变成下面这样

  var a;
  function a(){
     alert(10)
  } 
  alert(a)    1
  a();   2
  a=3;
  alert(a);   3    
  a=6;
  a();   4

很明显,会得到下面的结果
1. f a(){alert(10)} //变量提升,并且函数优先级高于变量,如果相同命名,变量会被覆盖。
小知识:

    console.log(c)//ƒ c(){console.log('hahha') 
    function c(){
        console.log('hahha')
    }
    var c = 1;

    console.log(c) //ƒ c(){console.log('hahha') 
    var c = 1;
    function c(){
        console.log('hahha')
    }
  1. 10 //执行了a(){alert(10)}
  2. 3 //给a赋值了3
  3. a is not a function 此时已经给a赋值了6,a已经不是函数了,所以会弹出错误
关于函数变量提升的一些特殊情况
    function yideng() { 
        console.log(1);
    }
    (function () { 
        if (false) {
            function yideng() {
                console.log(2);
            }
        }
        yideng();
    })();
    //yideng is not a function
在早期的浏览器中,弹出来的结果应该是2,(因为变量提升,整个         function yideng() { }会被提升到整个函数作用域的顶端)但是如果弹出来是2的话,我们写的if语句就完全没有意义了。后来浏览器为了修正这个错误,像这种情况下面的函数提升就只是 `var yideng`提升到函数作用域的顶端,本题中false所以没进入函数体,所以yideng()就会报错yideng is not a function。而不是像第一题那样,整个函数体都提到前面。

据说除了if,好像while,switch,for也一样。
补充小知识:

    if(false){
        var a = 1;
    }
    console.log(a)//undefined
关于this

写出以下输出值,解释为什么?

this.a = 20; 
var test = {
    a: 40,
    init:()=> {
        console.log(this.a);  
        function go() {
            console.log(this.a);
        }
        go.prototype.a = 50; 
        return go;
        }
    };
    new(test.init())();   //20   50 

注意:test是一个对象,对象下面的函数是箭头函数的话,this是指向全局的,因为js只有函数作用域,而对象的括号是不能封闭作用域的。所以此时的this是指向全局的。
go本身没有a 这个属性,所以new出来的对象也没有这个属性,会到原型链上面去找。所以是50。

  this.a = 20; 
  var test = {
    a: 40,
    init:()=> {
        console.log(this.a); 
        function go() {
           this.a = 60;
            console.log(this.a);
        }
        go.prototype.a = 50; 
        return go;
        }
    };
    var p = test.init();  //20 
    p();  //  60 
    new(test.init())();   //  60,60

刚开始我以为输出的结果会是20 60 20 60
var p = test.init(); 此时test.init()会执行一遍。此时的箭头函数中的this绑定了最外层的this(test父级作用域的this),也就是this.a = 20
p() 此时相当于执行go(),注意:这个时候是var p = test.init(),所以执行p()时的this是指向windows,也就是我们现在看到的最外层的this.a = 20的那个this,此时会执行this.a = 60,将windows的this.a 更改成60,执行之后打印出来的this.a=60.
new(test.init())():test.init()会取到已经绑定了的父级词法作用域的this,不过此时的this.a已经被改为60,所以打印出来60。继续执行,打印出60。

思考一下,如果是下面这种情况的话,会打印出来什么呢?

image.png

执行的时候20
因为这里是var 不是this.a

另外一个小知识:

    function test(){
        console.log(this)
    }
    var obj = {
        f:test
    }
    (obj.f)()  //Cannot read property 'f' of undefined

?????什么鬼??
原来是因为没有加;在浏览器看来
var obj = {f:test}(obj.f)() 是这样连在一起的。那肯定会报错啊!

加上分号之后,我们再来看一下这段代码运行的结果是怎么样的。

    function test(){
        console.log(this)
    }
    var obj = {
        f:test
    };
    (obj.f)()//{f: ƒ}

很明显,指向了obj这个对象,因为是obj调用了test()。
那如果我们再这样改一下:

    function test(){
        console.log(this)
    }
    var obj = {
        f:test
    };
    (false || obj.f)()

猜猜结果是什么?是window
?????这又是什么鬼???
短路语句的结果不也是obj.f,运算的结果应该也是obj才对啊。
但是()里面是计算,所以里面应该是表达式,也就是说里面相当于var xx = obj.f,然后xx(),此时this肯定是指向全局变量window

关于闭包块级作用域

请写出如下点击的输出值,并用三种办法正确输出li里面的数字。

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
</ul>
<script type="text/javascript">
    var list_li = document.getElementsByTagName("li"); 
    for (var i = 0; i < list_li.length; i++) {
        list_li[i].onclick = function() { console.log(i);
        }
    }
</script>

真的是很经典的一道题啊!!!!大家可以闭着眼睛说不管点击哪个li都只会输出6。(跟Js的单线程和异步队列有关)
解决方法有三个:

  • ES6的let

      for (let i = 0; i < list_li.length; i++) {
          list_li[i].onclick = function() { 
              console.log(i+1);
          }
      }
    

关于let 可以看看暂时性死区这个知识点。

  • 闭包,立即执行函数

    for(var i = 0; i < list_li.length; i++){
      (function(i){
          list_li[i].onclick = function(){
              console.log(i+1)
          }
      })(i)
    }
    

闭包保存这个变量。

  • 最后一种方法是佳哥说出来的时候恍然大悟,这才是最in的解决方案啊我靠。这道题考的是this啊,不是闭包也不是作用域。。。

      for (var  i = 0; i < list_li.length; i++) {
          list_li[i].onclick = function() { 
              console.log(this.innerHTML);
          }
      }
    
按地址传递和按值传递

函数按值传递???
https://blog.csdn.net/qq_35087256/article/details/79762618
写出输出值,并解释为什么

    function test(n){
        n = {
            v:5
        };
    }
    var m = {
        k:30
    };
    test(m);  
    alert(m.v); //undefined

??????占位先
m = {v:5};重写了m,是在堆内存空间中新建了一个Object()。注意的是,在这个函数里n和我们的实参m指向的并不是同一块内存了,n不管什么操作都和m没有关系了,也就是说在这个函数里面,只是新建了一个局部变量n,并没有真正对m进行说明操作。我们可以看到打印出来的m{k:30},证明了这个观点。

有点蒙圈?不用怕,我们继续看看。

    function test(n){
        n.v = 5;
    }
    var m = {
        k:30
    };
    test(m);  
    alert(m.v); //5

这里的m,n指向同一个内存,对n进行操作肯定也会对m造成影响~~~~

    var a = 1;
    var b = a;
    b = 2;
    console.log('a',a);  //1
    console.log('b',b);  //2

很明显,这是按值传递,b对a并没有造成任何影响。

    var a = {
        num : 1
    };
    var b = a;
    b.num = 2;
    console.log('a',a); //{num:2}
    console.log('b',b); //{num:2}

很明显,这是按引用传递,改动b就相当于改动a.

    var a = {
        num : 1
    };
    var b = a;
    b = {};  
    b.num = 3;
    console.log('a',a); //{num:1}
    console.log('b',b); //{num:3}

此时 b = {}开辟了新的内存地址。

弄清楚了按值传递和按引用传递,我们再来看看函数参数是怎么个按值传递法?
demo一:
在向参数传递 基本类型 的值时,被传递的值会被赋给一个局部变量(即命名参数/arguments对象中的一个元素):

    function add(n) {
        return n += 100;
    }

    var _count = 100;
    var result = add(_count);
    console.log(result);    //200
    console.log(_count);    //100 

_count没有变化,因为是值传递,内部的变化不会反映到函数外部。
demo二:
在向参数传递 引用类型 的值时,好像感觉有点奇怪:

    function setName(p) {
        p.name = 'hello';
    }

    var person = new Object();
    setName(person);
    alert(person.name);     //hello

我知道哪里怪怪的了。参数传递不是按值传递的吗?为什么内部 obj 的变化,会反映到外部的 person 对象上?
其实,在向参数传递 引用类型 的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部。
也就是说以上代码创建的一个对象并保留在一个变量 person中。然后,person在执行setName()函数时被复制给了p,在这个函数内部,pperson引用的是同一个对象。即使变量person是按值传递的,p也会按引用来访问同一个对象。
所以,当执行p.name = 'hello',外部的person.name => hello,因为person所指向的对象在堆内存中只有一个,而且是全局对象。

demo三:
好像demo2说明函数参数是按引用传递的,不然函数内部的变化怎么在外部也能检测得到呢?其实这是错误的:

    function setName(p) {
        p.name = 'hello';
        p = new Object();
        p.name = 'world';
    }

    var person = new Object();
    setName(person);
    alert(person.name);     //输出仍然是 hello

上面的例子很好的解释了函数参数的 按值传递
即使在函数内部修改了p的值,但是原始的引用依然保持不变的。

当在函数内部重写p的时候,这个变量的引用就是一个局部对象了。
也就是说,这个时候,p和person指向的是不同的地方,p的任何操作(增删查改)跟person是没有任何关系的
而,这个局部对象,会在函数执行完毕后自动销毁。

demo四:

    function setName(p) {
        p = {
            name : 'world'
        }
        p.name = 'hello';
    }

    var person = new Object();
    person.age = 10;
    setName(person);
    console.log(person);
    console.log(person.age);  //10
    console.log(person.name);     //undefined

本来p和person开开心心指的同一块内存,但是p重写了!重写是什么概念,就是p指向了一块新的内存,接下来不管p有什么操作,和person都没有关系,所以我们打印出来person,可以看到他还是和原来一样。{age:10}。person.name就还是undefined了。

我们下面再来看看js函数传递参数(Javascrpt 高级程序设计书中原题).
传参数的情况:

    function setName(obj) {
        obj.name = 'Nicholas';
        obj = new Object();
        obj.name = "Greg";
        console.log(obj.name); // "Greg"
    }
    var person = new Object();
    setName(person);
    alert(person.name) // "Nicholas"

没有传参数的情况:

    function setName() {
        person.name = 'Nicholas';
        person = new Object();
        person.name = "Greg";
        console.log(person.name); // "Greg"
    }
    var person = new Object();
    setName();
    console.log(person.name) // "Greg"

wtf???什么鬼?为什么没有传参数和传参数区别这么大的?
不要急,且听我慢慢道来。

person这个object作为参数传递给function的时候,function内部的作用域可以找到person而且这个时候obj的值是一个指向的是“堆内存空间中person所指向的这个Object它本身”(person本身也是个指针,obj是person这个指针被作为参数传递后复制出来的副本);然后下一步new出一个Object的时候,是在堆内存空间中新建了一个Object(),这个时候obj是指向这个新new出来的Object的指针,但是!person依旧是指向原来的堆内存,所以对这个新Object新增一个叫name的属性的时候对person并没有任何影响。需要注意的是,在function执行的这一个阶段里,对于原来的Object进行操作只有一次,即给它的name属性赋值为'Nicholas',随后的操作是对另外一块新的内存进行操作了。所以在外部的输出就是"Nicholas";

从始至终person伴随着我们的一直是person这个指针, 最初在声明阶段即在function外部那个 var person = new Object(); 时,这一步它是指向new出来位于堆内存内的Object。但在之后function中的执行阶段, person指针又被指向function里new出的这个Object了,注意这次不是局部变量,所以person就一直指向新的Object()了,所以后边的log输出就是这个新new出来的对象的name: "Greg" 。

如果,我想让第一段代码的两个name都能打印出来,就只要用一个全局变量来存储就可以在外面访问了。

写代码要聪明

请用一句话算出0-100之间学生的学生等级,如90-100输出为1等生、80-90为2等 生以此类推。不允许使用if switch等

当然,用if-else或者switch是可以实现的,但是你们不觉得代码量很多,显得有点傻吗?

    10-Math.floor(1/10) || 1

(感谢老司机_af2f的建议)
用这句代码就搞定了!!!

有这样的一段代码:

    var a = "test";
    if(a == "test"){
        console.log(1);
    }else if(a == "qq"){
        console.log(2);
    }

我们可以直接改写为:

    var obj = {
        "test": function(){
            console.log(1)
        },
        "qq": function(){
            console.log(2)
        }
    }
    var s = "test";
    obj[s]()

也太简单命明了了吧!这个问题需要一直注意下去。
如何保持代码的简洁,如何让代码更加聪明。

有这么一句话,叫做,只要超过了两层的if-else的代码那都不是好代码。可以看看书。《代码简洁之道》 《重构》

数组方法

请问已经一句话遍历变量a(禁止使用for 已知var a = "abc")

    var a = "abc";
    Array.prototype.slice.call(a)

借用数组原型链上面的方法。

    var a = "abc";
    [].forEach.call(a,item=> {
        console.log(item)
    })

让数组方法指向我们要遍历的a

    var a = "abc";
    [...a].map(item=>{
        console.log(item);
    })

把a变成数组。

    var a = "abc";
    Array.from(a).map(item => {
        console.log(item)
    })

把a 变成数组。
Array.from()方法从一个类似数组或可迭代对象中创建一个新的数组实例。

console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]

console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]
关于继承

-请在下面写出JavaScript面向对象编程的混合式继承。并写出ES6版本的继承。
要求:汽车是父类,Cruze是子类。父类有颜色、价格属性,有售卖的方法。Cruze子类实现父类颜色是红色,价格是140000,售卖方法实现输出如下语句:将 红色的Cruze买给了小王价格是14万。

 var Car = function(color,price){
    this.color = color;
    this.price = price;
}
Car.prototype.sale = function(){
    console.log(this.color + '色的车卖了'+this.price)
}
var Cruze = function(color,price){
    Car.call(this,color,price);
}
//需要解决的问题有
//1.拿到父类原型链上面的方法
//2.不能让构造函数执行两次
//3.引用的原型链不能按地址引用(不然子类上面的修改会影响到父类,可以使用Object.create来给做个副本)
//4.修正子类的constructor 
var __pro = Object.create(Car.prototype);//复制原型链
__pro.constructor = Cruze;
Cruze.prototype = __pro;
var m = new Cruze('red',14);
console.log(m);
m.sale()

ES6写法

class Car{
    constructor(color,price){
        this.color = color;
        this.price = price;
    }
    sale(){
        console.log(this.color +'色的车卖了'+this.price)
    }
}
    class Cruze extends Car{
        constructor(color,price){
            super(color,price);
        }
    }

    var bmw = new Cruze('red','199万');
    bmw.sale()

这里的super指的是Cruze而不是Car

异步嵌套

请你写出如何利用EcmaScript6/7(小Demo)优化多步异步嵌套的代码?


image.png
image.png
最后一题
    var length = 10; 
    function fn() {
        console.log(this.length);
    }
    var yideng = { 
        length: 5,
        method: function (fn) { 
            fn();
            arguments[0]();
        }
    };

    yideng.method(fn, 1); //10 2 

咦,为什么是2???好奇怪啊!
其实arguments是类数组对象{0:{console.log{},1:1},arguments0是arguments在调用这个fn函数,自然而然就是指向arguments了,如果是yideng.method(fn, 1,2)那么将会输出3,也就是实参的个数。

如果我们把代码改成下面这个样子?

    function fn() {
        console.log(this.length);
    }
    var yideng = { 
        length: 5,
        method: function (fn) { 
            fn();
        }
    };

    yideng.method(fn);

咦?这个时候执行fn的this还是指向window的。window.length是啥?
length 属性返回在当前窗口中frames的数量(包括IFRAMES)。
没想到吧!!所以这个时候的输出就要看有多少个iframe了。

参考博文和资料:
京程一灯的资料~
https://blog.csdn.net/qq_35087256/article/details/79762618

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容