js学习笔记

js代码执行分为两个阶段:预编译和代码执行

**预编译:**

声明提升:加var的变量以及function声明的函数都会提升,提升到代码段的最前面。

函数内部的局部变量,提升到函数体的最前面。

注意:1、变量的提升仅仅提升声明,函数是提升整个函数体。

代码执行:自上而下执行

全局变量和局部变量的分界点是函数!

没有加var的变量都是全局变量  不管是函数里面还是函数外面

1.形参和变量声明

2.实参值赋给形参

3.寻找函数声明

4.执行函数

**全局执行上下文:**

        当全局代码执行时,就会产生一个全局的执行上下文,EC(G);数据保存在VO中(变量对象)

**局部执行上下文:**

        当函数代码执行时,就会产生一个局部的执行上下文,EC(Fn)。只要调用一个函数,就会产生一个局部执行上下文。调用100个函数,就会产生100个执行上下文。执行上下文里会保存一些数据

内存分为栈堆,基本数据类型存在栈 引用数据类型存在堆,栈里存的引用类型的地址

形参就是函数内部加var的变量

当执行上下文出栈后就被销毁(数据存储所占用的内存空间都要被释放)

var a=b=2--->var a=2;b=2;

##加var的变量和不加var的变量区别

1.加var的变量在预编译期间会提升,不加var的变量不会提升。

2.不管有没有var,创建的变量都会放在GO中,都可以通过window.XX 拿到变量。

3.加var的变量可能是全局变量也可能是局部变量,不加var的只能是全局变量。

4.加var的局部变量不会作为window的属性

let不允许重复声明 const不允许修改

##作用域

局部:函数内部 AO

全局:函数外部

- GO 全局对象

- ECS 执行上下文栈 先进后出(杯子)

- ES(G)全局执行上下文,当执行全局代码时就会产生全局执行上下文(鸡蛋,放入杯子中)

- EC(fn)当函数代码执行时,就会产生一个局部的执行上下文

如果全局函数在if中,仅仅提升声明,不提升赋值。

如果条件成立,进来的第一件事就是赋值

```

//预编译:提升

//首先var fn提升了 fn的值是undefined

//下面的fn函数整体(函数名+函数体)也要提升

//如果提升过了,就不在提升,函数名不在提升

//函数体提升 fn的值由undefined变成函数体

console.log(fn); //打印函数

var fn=100;

function fn(){

  console.log("fn...")

}

```

函数没有返回值,默认返回undefined

形成闭包的条件:是一个不会释放栈内存空间的EC(局部的执行上下文)

闭包指的是栈空间

当内部函数被返回到外部并保存时,一定会产生闭包,闭包会产生原来的作用域链不释放,过度的闭包可能会导致内存泄漏或加载过慢.

在一个函数里面嵌套一个函数,并且里面的函数引用了外部函数的变量,这样就形成了闭包

闭包的前提是函数嵌套

一个不会释放内存的栈空间就是一个闭包

闭包好处

1.保存变量的值(延长一个函数内部变量的生命周期)

2.由于x是函数内部的变量,外部不能访问。对x有保护作用

```

// 闭包实现累加器

function test(){

    var num=0;

    function add(){

        console.log(++num);

    }

    return add;

}

var add=test();

add();  //1

add();  //2



//闭包实现缓存机制

function myClass(){

    var students=[];

    var operations={

        join:function(name){

            students.push(name);

            console.log(students);

        },

        leave:function(name){

            var idx=students.indexOf(name);

            if(idx>-1){

                students.splice(idx,1);

            }

            console.log(students);

        }

    };

    return operations;

}

var obj=myClass();

obj.join('1111');

obj.join('2222');

obj.leave('1111');

```

作用域链:数据的查找机制

IIFE:

立即调用函数表达式

(function(){})();

(function(){}());

```

var f=function(){};//普通函数表达式

function g(){};//函数声明、定义

```

一定是表达式才能被执行符号执行

var num=(1,2);返回2 ()里面有, 只返回,后面的值

(function b(){}) 带()代表是表达式,忽略函数名字(b)


this:

1.如果this出现在普通函数中,通过window.f调用函数,那么this就表示window,主要是看.前面是谁

2.事件绑定中,监听器中的this表示事件源

3.对象是属性的无序集合,在一个对象中也可以有函数,如果一个函数出现在了对象中(方法),那么this表示这个对象

4.在IIFE中,this表示window

5.只有代码执行时才知道this指向

in:

也是一个运算符,判断一个属性是否属于某个对象,不管是共有属性还是私有属性

new的原理

1.内部创建一个空对象

2.内部的this指向这个空对象

3.返回这个空对象

原型和原型链

1.每一个对象中必定有一个属性叫__proto__,指向构造器的原型函数

2.每一个构造器上,都有一个属性叫prototype,也是指向原型对象上,原型对象上通常会放有公共属性

3.每一个原型对象上,都有一个constructor属性,指向他所对应的构造器

4.当我们在查找一个对象上的属性时,先在自己的私有属性中找,如果找不到,就沿着__proto__去原型对象中找,如果原型对象中找不到,就再沿着__proto__去它的原型对象的原型对象中找,直到null。如果找不到就报错undefined。(原型链)

当我们修改了原型对象,需要手动修改constructor的指向

不是所有的对象都继承于object.prototype    Object.create(null)

原型链的顶端是object.prototype

==会自动调用toString方法

.的优先级高于new

call:改变函数中this的指向

方法.call(obj)  1.让this指向obj 2.让方法执行

apply:和call的作用是一摸一样的,只是穿参的形式不一样

当函数的参数大于3个时,使用call性能更好

callee在哪个函数里面值得是哪个函数本身,通过arguments.callee调用函数自身

caller返回当前被调用函数的函数引用

```

  function say(a,b){

      console.log(a,b)

  };

  let obj1={name:"aaa"};

  let obj2={name:"bbb"};

  say.call(obj1,1,2);

  say.apply(obj2,[2,3]);

```

typeof:

object/string/number/boolean/function/undefined

优点:对基本数据类型和函数判断很准确;是运算符 简单

缺点:对引用数据类型判断不准确,结果全是object

使用call优化

```

function type(data){

  return Object.prototype.toString.call(data)

}

```

继承:

原型继承 :

1.继承父类的共有属性和私有属性作为子类的共有属性;

2.核心代码:Son.prototype=new Father(); Son.prototype.constructor=Son;

call继承:

1.继承父类的私有属性作为子类的私有属性

2.function Son(){

Father.call(this)

}

组合继承:call+原型

1.继承父类的共有和私有属性,作为子类的共有属性  原型

2.继承父类的私有属性,作为子类的私有属性  call

3.父类的私有属性被继承了两次,当我们去访问一个属性时,会先访问私有属性

只继承父类的共有属性,作为子类的共有属性

var Fn=function(){}; //空函数 中介作用

fn.prototype=Father.prototype; //改变Fn的原型对象,和Father共享一个原型对象

Son.prototype=new Fn();

```

//圣杯模式继承

function Teacher(){

    this.name='Mr.Li';

    this.tSkill='JAVA';

}

Teacher.prototype={

    pSkill:'JS'

}

var t=new Teacher();

console.log(t);

function Student(){

    this.name='Mr.Wang';

}

//中间缓冲构造函数,作用是原型继承的时候与原本的原型隔离开

// function Buffer(){}

// Buffer.prototype=Teacher.prototype;

// var buffer=new Buffer();

// Student.prototype=buffer;

// //此处只改变student的原型,不影响teacher的原型

// Student.prototype.age=18;

inherit(Student,Teacher);

var s=new Student();

console.log(s);

//函数封装

function inherit(Target,Origin){

    function Buffer(){};

    Buffer.prototype=Origin.prototype;

    Target.prototype=new Buffer();

    Target.prototype.constructor=Target;

    Target.prototype.super_class=Origin;

}

```


类数组

把{}变成[] 需要在对象中加入splice,并且继承数组原型的splice方法,还要有length属性

Object.prototype.toString.call('') 增强版typeof

```

//类数组面试题以及原理解析

var obj={

    '2':3,

    '3':4,

    'length':2,

    'splice':Array.prototype.splice,

    'push':Array.prototype.push

}

obj.push(1);

obj.push(2);

console.log(obj);

// {

//    '2': 1,

//    '3': 2,

//    length: 4

// }

//push原理解析

// Array.prototype.push=function(elem) {

//    this[this.length]=elem;

//    this.length++;

// }

```

Array.from:把伪数组变成真实数组

数组的下标 从前面开始数是从0开始的 从后面开始是从-1开始的

可迭代对象: 可以使用for of 进行遍历

数组 字符串 Set Map

不可迭代:

object

push/unshift(最后/最前面 添加元素) 返回值,是执行了方法以后的数组长度 修改原数组

pop/shift(删除最后/最前面一位元素,没有参数)返回值 删除剪切的元素 修改原数组

reverse (倒序) 返回值,改变后的数组 修改原数组

splice(开始项的下标,剪切长度,剪切以后的最后一位添加的元素) 返回值 剪切的元素 修改原数组

sort(按照ascii码排序)返回值 排序以后的结果 修改原数组

concat(合并数组) 返回值改变后的数组 新建数组,不影响原数组(新建)

slice(截取起始下标,截取结束下标之前一位)返回值是截取的数组,不影响原数组(新建)

join(数组转成字符串)返回值是转换之后的字符串,不影响原数组(新建)

split(按什么分隔的,分隔的长度) 返回值转换之后的数组,不影响原数组(新建)

```

//1.参数a,b

//2.返回值:正值,b排前面

//              负值,a排前面

//                  0,保持不动

var arr=[27,39,5,7];

arr.sort(function(a,b) {

    //return a>b?1:-1

     return a-b //升序 b-a降序

})

console.log(arr);

//随机排序

//Math.random()->0-1之间的数,不包含0和1 开区间

var arr=[1,2,3,4,5,6,7,8];

arr.sort(function(a,b){

   //var rand=Math.random();

    //return rand-0.5>0?1:-1;

      return Math.random()-0.5;

})

console.log(arr);

```

find方法:

    在一个数组中,找出第一个符合条件的元素。

    返回值:返回找到的元素  如果说没有找到,就返回und

    let arr = [1,2,3,4,5]

    let res = arr.find(item=>{  //在arr中,找出大于2的元素

        return item>100

    })

    console.log(res)

--------------------------------------------------------

findIndex方法:

    在一个数组中,找出第一个符合条件的元素的索引。

    返回值:返回找到的元素的索引  如果说没有找到,就返回-1

    let arr = [1,2,3,4,5]

    let res = arr.find(item=>{  //在arr中,找出大于2的元素

        return item>100

    })

    console.log(res)

--------------------------------------------------------

includes方法:

    判断一个数组中是否包含某个值,如果包含,返回true。

--------------------------------------------------------

forEach方法:

    遍历数组    声明式编程

--------------------------------------------------------

map方法:

    对数组中的每一项元素进行加工,返回一个加工后的新数组。

    let arr = [1,2,3,4,5]

    let res = arr.map(item=>{

        return item*100

    })

    console.log(res)

--------------------------------------------------------

filter方法:

    filter本意是过滤的意思。过滤出满足我们条件的元素,形成一个新的数据。

--------------------------------------------------------

some方法:

    遍历数组中每一项,有一项返回true,那么就停止遍历,整体的结果就是ture

--------------------------------------------------------

every方法:

    遍历数组中每一项,所有项返回true,整体结果才为true

每个函数作用域链上都有GO,函数自己的AO排在第一位,GO排第二位

var i=1;

var a=i++; //a=5  解析:var a=i;i=i+1;

var b=++i; //b=2  解析:i=i+1;var b=i;


window.a||(window.a='1');console.log(a);

//结果是a 有()先看(),a=1,然后再去看或者window.a

1.形参和变量声明

2.实参值赋给形参

3.寻找函数声明

4.执行函数

undefined=>false

数组截断方法

```

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

arr.length=3;

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

```

原始值没有属性和方法

```

;(function(){

    var Compute=function(opt){

        this.x=opt.firstNum || 0;

        this.y=opt.secondNum || 0;

    }

    Compute.prototype={

        plus:function(){

            return this.x+this.y;

        },

        minus:function(){

            return this.x-this.y;

        },

        mul:function(){

            return this.x*this.y;

        },

        div:function(){

            return this.x/this.y;

        }

    }

    window.Compute=Compute;

})();

var compute=new Compute({

    firstNum:1,

    secondNum:2

});

var res=compute.plus();

```

```

function Car(){};

var car=new Car();

function Person(){};

var p=new Person();

console.log(car instanceof Car);//true

console.log(car instanceof Object);//true

console.log([] instanceof Array);//true

console.log([] instanceof Object);//true

console.log({} instanceof Object);//true

```

```

window.onload=function(){

    init();

}

function init(){

    console.log(initFb(10));

    console.log(initDiv(100));

}

var initFb=(function(){

    function fb(n){

        if(n<=0){

            return 0;

        }

        if(n<=2){

            return 1;

        }

        return fb(n-1)+fb(n-2);

    }

    return fb;

})();

var initDiv=(function(){

    function div(n){

        var arr=[];

        for (let i = 0; i < n; i++) {

            if(i%3==0||i%5==0||i%7==0){

                arr.push(i);

            }

        }

        return arr;

    }

    return div;

})();

```

```

function deepClone(origin,target){

    var target=target||{},

        toStr=Object.prototype.toString,

        arrType='[object Array]';

    for (const key in origin) {

        if (origin.hasOwnProperty(key)) {

            if(typeof(origin[key])==='object'&&origin[key]!==null){

                if(toStr.call(origin[key])===arrType){

                    target[key]=[];

                }else{

                    target[key]={};

                }

                deepClone(origin[key],target[key]);

            }else{

                target[key]=origin[key];

            }

        }

    }

    return target;

}

```

```

var name ='222';

var a={

    name:'111',

    say:function(){

        console.log(this.name);

    }

}

var fun=a.say;

//var fun=function(){

//    console.log(this.name);

// }

fun();//this指向window 打印222

a.say();//this指向a 打印111

var b={

    name:'333',

    say:function(fun){

        fun();

        //此处是fun函数调用,fun函数是全局的,所以this还是指向window->222

        //+function(){

        //    console.log(this.name);

        // }()

    }

}

b.say(a.say);//222

b.say=a.say;

b.say(); //333

```

```

function Foo(){

    getName=function(){

        console.log(1);

    }

    return this;

}

//相当于给函数对象增加属性

Foo.getName=function(){

    console.log(2);

}

Foo.prototype.getName=function(){

    console.log(3);

}

var getName=function(){

    console.log(4);

}

//预编译的时候会变量提升

function getName(){

    console.log(5);

}

Foo.getName(); //2

getName();//4

Foo().getName();//1

getName();//1

new Foo.getName();//.的优先级高于new 相当于Foo.getName() new2没有意义 还是2

new Foo().getName();//()的优先级比.大 实例化了函数,函数本身没有getName属性,故去找原型 3

new new Foo().getName(); //同上 new3没有意义 还是3

```

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