JavaScript高级部分概念用法

一、执行上下文

也称为可执行代码和执行上下文
执行代码:1.全局代码 2.函数代码 3.eval代码
eval("var a = 200;console.log(a)")
执行上下文 - Context 
所处的一个环境,环境不同含义也随着改变了
当可执行代码执行的过程中,都会产生一个可执行环境
在执行完之后,执行上下文的环境也随着销毁
执行上下文中变量存在于:
变量对象:VO - var 声明的一个属性值 - 全局 
活动对象:AO - 相对于函数创建的对象中的一个声明 - 局部 - 随执行完后销毁
不管变量在什么地方声明,都会在函数一运行就声明一个函数中的变量
变量提升:指一个函数上下文创建时,函数中的所有变量都会随函数的创建提升
    var x = 100;
    var outFunc = function(){
        x++;
        console.log(x);//NaN
        var x = 200;
    }
    outFunc();
    console.log(x);//100
函数中 x 声明提前,但是赋值并没有提前,所有此时的x为undefined,undefined进行运算结果就为NaN

var  outFunc = function(){
    var a = 100;
    var innerHtml = function(){
        var b = 200;
        a = b;//a替换了outFunc()中a var a = 100的值 此时a = 200;
//      c = a;
    }
    innerHtml();
    console.log(["inner:",a]);//200
}
outFunc();//该函数在运行完后函数中的变量随之销毁
console.log("outer:",a);//not defined

二、作用域 作用域链

作用域:即为代码的作用范围
作用域链:函数的变量不使用var声明的情况下,声明变量会一直往上查找该变量的值,直到全局变量还没有就创建一个全局变量
Scope对象 - 存放上一层中的对象引用 - 属于某一个函数的属性,当函数一创建就已经有该函数的scope对象引用了
每个函数都包含自己本身的VO、Scope对象、作用域链对象
var food = "包子";
var eat = function(){
    console.log(food);//包子
}
(function(){
    var food = "油条";
    eat()
})

var foo = 1;
function bar(){
    if(!foo){
        var foo = 10;//foo会变量提升到bar函数中,但值不会提升此时提升的foo为undefined !foo 刚好满足条件
    }
    console.log(foo);
}
bar();

var a = 1;
function b(){
    a = 10 ;//a = a()
    return;
    function a(){}//会变量提升到函数的前面
}
b();
console.log(a);

var f = true;
if(f === true){
    var a = 10;
}
function fn(){
    var b = 20;
    c = 30;
}
fn();
console.log(a);//10
console.log(b);//not definnd
console.log(c);//30

if('a' in window){
    var a = 10;//变量提升至if判断外,但值不会提升;没有var 不会先变量提升会先判断是否进入判断
}
console.log(a);//10

var a = b =3;
(function(){
    var a = b = 5;//var a = 3; b = 5
})();
console.log(a);//3
console.log(b);//5

var foo = 'A';
console.log(foo);   //A
var foo = function(){
    console.log('B');
}
console.log(foo);   //function(){console.log('B');}
foo();              //B
function foo(){     //函数是在最开始的时候就存在了,但在var foo 声明后被覆盖了,已经被提升;会影响代码的上下顺序
    console.log('C');
}
console.log(foo);   //function(){console.log('B');} - 
foo();              //B

var a = 1;
function b(){
    console.log(a);//undefined
    a = 2;
    console.log(a);//2
    var a = 3;
    console.log(a);//3
}
console.log(a);//1
b();
console.log(a);//1

//闭包相关的函数值
var x = 100;
var y = 200;
function funcA(x){
    var y = 201;
    function funcB(){
        console.log(x);//101
        console.log(y);//201
    }
    return funcB;
}
var f = funcA(101);
f();  

三、this关键字

this 也称为当前对象,是用于对象当中,属于第一人称,所处的环境不一样指代的含义也不同
如果在对象的方法中只要在嵌套的函数中的this就不会再指向当前对象中的this了
比如:
var name = "张三";
var func = function(){
    console.log(this.name);//此时的this指向的是全局变量
}
func();
fnnc.apply();
func.call()
var obj = {
    name:"王五",
    func:function(){
        console.log(this.name);//此时的this 指向的是对象中的name
        (function(){
            console.log(this.name);//此时的this指向的不是对象的name,而是全局变量
        })()
    }
}
obj.func();

改变this指向的方法:
1.call
2.apply
两者的作用是相同的,可以帮助完成方法的调用,默认this指向为全局,要想访问嵌套对象,就将其赋值给一个变量
call() Or apply()可以改变this的对象,第一个参数是不能省的
区别:
var sum = function(a,b){console.log(a+b)}
sum.call(null,100,200) - 采用的是参数列表
sum.apply(null,[100,200]) - 采用的是数组

***练习***
var myObject = {
    foo:"bar",
    func:function(){
        var self = this;
        console.log(this.foo);//bar
        console.log(self.foo);//bar
        (function(){
            console.log(this.foo);//undefined
            console.log(self.foo);//bar
        })()
    }
}
myObject.func();

var user = {
    count:1,
    getcount:function(){
        return this.count;
    }
}
console.log(user.getcount());//1
var func = user.getcount;//func就代表user.getcount这个函数
console.log(func());//undefined - func()已经成为全局,全局中没有count这个变量,所以值为undefined

四、闭包 - closure

闭包:是指能够访问函数内部变量的函数,定义在函数内部的函数。
一个函数引用了外部的自由变量,那么这个函数就叫闭包,被引用的函数和引用的函数是一同存在的。
自由变量 - 跨作用域的变量或父级的变量
函数必须引用外部变量,函数还必须被引用才能成为闭包
优点:可以把一个局部变量存在的时间延长,进行持续保存
缺点:如果大量的使用闭包,持续保存的变量会一直占有内存,造成内存的浪费
常用:事件处理常常会使用到闭包

var lis = document.getElementsByTagName("li");
for(var i = 0;i<lis.length;i++){
    //方法实现一
    (function(index){//阻止闭包
        lis[index].onclick = function(){
            console.log("这是选中的地"+(index+1)+"项");
        }
    })(i)
    //方法实现二
    var func = function(index){
    lis[index].onclick = function(){
            console.log("这是选中的地"+(index+1)+"项");
        }
    }
    func(i);
}

//闭包练习
function Foo(){
    var i = 0;
    return function(){
        console.log(i++);//i++先赋值再运算
    }
}
var f1 = Foo();
var f2 = Foo();
f1();//0
f2();//0
f2();//1

五、面向对象 - OO - Object Oriented

语言分类大致分为两大类 - 范式
1.命令式 - 通过语言告诉计算机应该如何做事情
比如:java、C语言(为程序语言的发展做出贡献)
命令式的两种思想:
    1.1.面向过程 - 将过程逐一分解为一个一个的步骤执行 - 计算机的思维方式为主体
    缺点:人的思维有限,如果程序实现的过程很复杂,人会不能完全考虑 
    1.2.面向对象 - 本身就是人的思考方式,人的思维为主体,从自身角度出发 - 特征、行为 - 万物皆对象,对象因关注而产生

2.声明式 - 告诉计算机我想要什么,然后计算机进行相关的动作,然后计算机自己进行运算得到我想要的结果
比如:Css
声明式的三大类:
    2.1.领域特定语言 - DSL - 在特定范围的语言 - HTML、Css、SQL、正则表达式
    2.2.函数式编程 - 类似公式,计算机就会按照这个公式进行计算并返回结果 
    与命令式编写相比,函数式编程更为精简,能够完善命令式编程的一些缺陷 - lisp、Haskell
    2.3.逻辑编程 - prolog - 记日志比较好的方法

构建对象的两种方式:
1.基于类的面向对象 - 拥有相同属性的划分为一类,类是对象的抽象,对象是类的实例
2.基于原型的面向对象 - JavaScript原型就有一个Object对象,通过一个原型克隆一个对象
好处:足够灵活
缺点:随意性太强,对于初学者容易出错
面向对象三大特征:
1.封装 - 需重点了解 - 指实现细节隐藏起来的过程就是封装 
    优点: - 但还需要考虑参数的问题
    1.1.隐藏实现的细节
    1.2.重用 - 不变的整合在一起,变化的作为参数

    JavaScript中属性都应为私有,方法可以为公共 - 由我们自己控制
    pulic - 公共,其他方法可以访问
    private - 私有,只能自己能够访问
    set/get - 访问器/修改器

    var Student = function(name,age,gender){
        this.name = name;
        var _age = age;//添加 _ 可将变量变为私有变量,外部不能随意访问
        var _gender = gender;
        if(!Student._init){
            Student.prototype.getAge = function(){
                return _age;
            }
            Student.prototype.setAge = function(age){
                if(age > 20 && age < 30){
                    _age = age;
                    console.log(_age);
                }else{
                    console.log("年龄修改不能在 20 - 30 之外");
                }
            }
            Student.prototype.getGender = function(){
                return _gender;
            }
            Student.prototype.setGender = function(gender){
                _gender = gender;
                console.log(_gender);
            }
        }
        Student._init = true;
    }
    var stu = new Student("张飞",20,"男");
    stu.name = "关羽";
    stu.setAge(31);
    console.log(stu.getAge());
    console.log(stu.getGender());
    stu.setGender("无");

2.继承 - 存在于有父与子的关系中 - 出现率较高 - 指采用一个对象的功能并且能够添加新的功能
    优点:
    2.1.复用
    2.2.扩展
    缺点:
    如果继承设计的不够完善,会致使变得复杂,难以操控
    继承的3种方法:
    1.对象冒充法 - instanceof - 判断是否为继承
    2.原型链 - 将自己的原型改变为父级的对象
    3.混合方式 - 
//  1.对象冒充法
    /*var People = function(name){
        this.name = name;
    }
    People.prototype.intro = function(){
        console.log("HI,我是"+this.name);
    }
    var ChinesePeople = function(name){
        this.inhert = People;
        this.inhert(name)
        delete this.inhert;
    }
    var info = new ChinesePeople("张三");
    console.log(info.name);
    console.log(info instanceof ChinesePeople);*/

//  2.原型链
/*  var People = function(name){
        this.name = name;
    }
    People.prototype.intro = function(){
        console.log("HI,我是"+this.name);
    }
    var ChinesePeople = function(name){

    }
    ChinesePeople.prototype = new People("张三");
    ChinesePeople.prototype.area = function(){
        console.log("我是中国人");
    }
    var info = new ChinesePeople("张三");
    console.log(info.name);
    console.log(info instanceof ChinesePeople);
    info.intro();
    info.area();*/

//  3.混合
    /*var People = function(name){
        this.name = name;
    }
    People.prototype.intro = function(){
        console.log("HI,我是"+this.name);
    }
    var ChinesePeople = function(name){
        People.call(this.name);
    }
    ChinesePeople.prototype = new People("张三");
    ChinesePeople.prototype.area = function(){
        console.log("我是中国人");
    }
    var info = new ChinesePeople("张三");
    console.log(info.name);
    console.log(info instanceof ChinesePeople);
    info.intro();
    info.area();*/
3.多态 - JavaScript本身就是一个多态的行为

var Student = {
    "name":"张飞",
    "age":20,
    "learn":function(){
        console.log(this.name+"学习JavaScript")
    }
}
Student.gander;//访问的属性不存在 值为 undefined
Student.learn();
Student.name;
Student["name"];
Student["lea"+"rn"];//动态的属性值使用中括号或者字符串拼接

//构造函数
var Student = function(name,age){
    this.name = name;
    this.age = age;
    this.learn = function(){
        console.log(this.name+" - "+this.age);
        var that = this;
        (function(){
            console.log(that);
        }())
    }
}
var stu1 = new Student("张飞",20);//stu1 为一个对象
var stu2 = new Student("刘备",21);
Student.prototype.gander = "男";//在原型中添加一个性别的属性
Student.prototype.play = function(){//在原型中创建一个play 的方法
    console.log(this.name+" - "+this.age+" - "+"喜欢玩游戏");
}
stu1.learn();
console.log(stu1.gander);
stu1.play();
stu2.learn();
Studetn.prototype;//可以改变一个对象真正的原型,但是不能多次调用
stu1.__proto__; //只是更改了stu1的引用原型,而不是本身的原型,只是对象的一个属性,可以通过属性一层一层的访问到对象的原型

对象 → 自定义对象原型 → Object对象 → Object原型 → Null

if(!Student._int){//首先判断Student._int是否为假,Student._int事先不存在即为假,进入判断,Student._int变为真,不在进入判断,该判断这样就只能判断一次
    Student.prototype.learn = function(){
        console.log(this.name+" - "+this.age);
    }
    Student.prototype.play = function(){//在原型中创建一个play 的方法
        console.log(this.name+" - "+this.age+" - "+"喜欢玩游戏");
    }
    Student._int = true;
}

两个运算符:new - 创建对象; delete - 删除属性 - delete.stu1.name;//stu1中的name属性就被删除了,只能删除原型创建的而不能删不是原型创建的属性
两个语句:with - 性能有一定的问题,尽量不使用; for..in - 不拿来循环数组;

var stu1 = new Student("张飞",20);//stu1 为一个对象
var stu2 = new Student("刘备",21);
for(var key in stu1){
    console.log([key,stu1[key]]);
}
原文地址:https://www.cnblogs.com/1039595699TY/p/5642080.html