JS基础 #第七天

目录

一、声明提升

变量的声明提升 

函数的声明提升

二、作用域

全局作用域

函数作用域

三、预编译

四、this的理解

五、工厂方法创建对象

六、构造函数

七、原型



一、声明提升

变量的声明提升 

        使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),但是如果声明变量时不使用var关键字,则变量不会被声明提前。

    console.log(a); // undefined
    var a = 4;
    // 以上代码相当于:
    var a;
    console.log(a);
    a = 4;

函数的声明提升

1.使用函数声明形式创建的函数  function函数(){ } :它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数

    //  函数声明, 会被提前
    fun(); // 会正常执行 
    function fun() {
      console.log("fun函数");
      
    }

2.使用函数表达式创建的函数,不会被声明提升,所以不能在声明前调用 

    // 函数表达式, 不会被声明提前
    fun2(); //会报错
    var fun2 = function() {
      console.log("fun2函数");
      
    }

二、作用域

作用域: 指一个变量作用的范围, 在JS中一共有两种作用域。

全局作用域

1.直接编写在script标签中的JS代码,都在全局作用域

2.全局作用域在页面打开时创建,在页面关闭时销毁

3.在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器

    创建, 我们可以直接使用

4.全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到

5.1.在全局作用域中, 创建的变量都会作为window对象的属性保存

    var a = 10;
    console.log(window.a); // 10

5.2在全局作用域中,创建的函数都会作为window对象的方法保存

    function fn() {
      console.log('11'); 
    }
    window.fn(); // 11

函数作用域

1.调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁。

2.每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的。

3.1在函数作用域中可以访问到全局作用域的变量

    var a = 10;
    function fun() {
      console.log(`a=${a}`); // a=10  

    }
    fun();

3.2在全局作用域中无法访问到函数作用域的变量

    function fun1() {
      var b = 2;
    }
    console.log(b); // 报错

4.当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则会报错!!

    var c = 10;
    function fun3() {
      var c = "作用在fun3";
      function fun4() {
        console.log(`c为: ${c}`); // c为: 作用在fun3       
      }
      fun4();
    }
    fun3();

5. 在函数里面访问全局变量: window.变量  

    var d = 20;
    function fn1() {
      var d = 3;
      // console.log(d);  3
      console.log(window.d); // 20
    }
    fn1();

6.在函数作用域也有声明提前的特性

6.1使用var关键字声明的变量,会在函数中所有的代码执行之前被声明

    function fn2() {
      console.log(m); // undefined
      var m = 34;
    }
    fn2();
    // 以上代码相当于
    function fn2() {
      var m;
      console.log(m);
      m = 34;
    }
    fn2();

6.2 函数声明也会在函数中所有的代码执行之前执行

    function fn4() {
      fn5();  // 221
      var a = 35;
      function fn5() {
        console.log(221);   
      }
    }
    fn4();

7.在函教中,没有使用var声明的变量,会向上一级寻找,如果都找不到,就会成为全局变量!!

    var n = 43;
    function fn6() {
      n = 12;
    }
    fn6();
    console.log(n); // 12

8.定义形参就相当于在函数作用域中声明了变量

    var e = 250;
    function fnn(e) {
      console.log(e);
    }
    fnn(); // undefined 有形参但没传递实参 所以是undefined

三、预编译

预编译类型含义
全局预编译在开始执行语句时对全局声明的变量进行预编译
局部预编译在函数调用的前一刻开始预编译

JS解释器执行js代码的步骤:  从上到下顺序执行

语法检查对字母大小写、括号匹配等语法检查
  预编译全局预编译创建一个GO对象,即全局的执行期上下文
把全局下声明的变量、函数名作为属性追加到该对象里面
局部预编译创建一个AO对象,即函数的执行期上下文
把函数内部声明的变量、函数名和形参作为属性,追加到该执行期上下文对象里面;
把实参的值赋值给对应的属性
把函数内部声明的函数作为值赋值给对应的属性
执行语句赋值语句、函数调用语句、循环语句、判断语句等

【面试题1】

    function fn(o) {
      o.siteUrl = "a";
      o = new Object();
      o.siteUrl = "b";
    }
    var cr = new Object();
    fn(cr);
    console.log(cr.siteUrl);
    /*
      代码解析:

      全局预编译:首先进行全局预编译,将全局下的变量、函数声明放在GO中
      GO:{
      fn: function() {},
      cr: undefined
      }

      局部预编译:全局预编译之后,开始执行语句,遇到函数的地方要进行局部预编译,
        将函数里面的变量、形参、函数声明放在AO中,将传递的实参赋值给形参,
        其余变量都为undefined
      AO:{
        无声明的变量,不需要局部预编译
      }
    */

图解:

四、this的理解

根据函数的调用对象不同,this会指向不同的对象,即谁调用,就指向谁

    var name = '和尚';
    function fn() {
      console.log(this.name); // window
    }
    // fn();
    var obj = {
      name: "八戒",
      sing: fn
    };
    obj.sing(); // 八戒    this指向obj

    var obj1 = {
      name: "悟空",
      sing: fn
    };
    obj1.sing(); // 悟空  this指向obj1

五、工厂方法创建对象

    function creat(namer, age, gender) {
      var obj = new Object();
      obj.namer = namer;
      obj.age = age;
      obj.gender = gender;
      obj.sing = function() {
        console.log(this.name);       
      }
      return obj;
    }
    var obj1 = creat('刘德华', 12, "男");
    var obj2 = creat('张学友', 18, "男");
    var obj2 = creat('张学友', 18, "男");
    console.log(obj1); // {namer: "刘德华", age: 12, gender: "男", sing: ƒ}
    console.log(obj2); // {namer: "张学友", age: 18, gender: "男", sing: ƒ}

六、构造函数

1.概念:构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是:

  • 构造函数习惯上首字母大写
  • 构造函数和普通函数的区别就是调用方式的不同
  • 普通函数是直接调用,而构造函数需要使用new关键字来调用

2.构造函数的执行流程:

     (1)立刻创建一个新的对象

     (2)将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建的对象

     (3) 逐行执行函数中的代码

     (4)将新建的对象作为返回值返回

3.使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类,我们将通过一个构造函数创建的对象,称为是该类的实例。

    function Person(namer, age, gender) {
      this.namer = namer;
      this.age = age;
      this.gender = gender;
      this.sing = function() {
         console.log('唱歌');  
      };
    // 创建实例
    var liudehua = new Person('刘德华', 12, '男');
    var zhangxueyou = new Person('张学友', 16, '男');
    var ljr = new Person('梁静茹', 23, '女');
    console.log(liudehua);  //  {namer: "刘德华", age: 12, gender: "男", sing: ƒ}
    console.log(zhangxueyou); // {namer: "张学友", age: 16, gender: "男", sing: ƒ}
    console.log(ljr); //  {namer: "梁静茹", age: 23, gender: "女", sing: ƒ}
    ljr.sing(); // 唱歌 调用方法

    // 所有的对象都是Object的后代,所以任何对象和Object做instanceof检查时都会返回true
    console.log(ljr instanceof Object); // true

七、原型

1.原型对象:就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中 !!

2.我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象

     function Class() {

      }
      var mc = new Class();
      console.log(Class.prototype); // {constructor: ƒ}

3. 如果函数作为普通函数调用, prototype没有任何作用

4.当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过_proto__来访问该属性

    console.log(mc.__proto__); // {constructor: ƒ}
    console.log(mc.__proto__ == Class.prototype); // true

5.当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用 

    // 给Class的原型对象添加一个属性
    Class.prototype.namer = 'koko';
    console.log(mc.namer); // koko   mc中也添加了这个属性

    // 给Class的原型对象添加一个方法
    Class.prototype.sing = function() {
      console.log("你好");
    }
    mc.sing(); // 你好    mc中也添加了这个方法

6.使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true

    function MyClass() {

    }
    // 向MyClass的原型对象中添加一个namer属性
    MyClass.prototype.namer = '我是原型中的属性';
    // 实例一个对象
    var mc = new MyClass();
    mc.age = '18'
    console.log(mc.namer); // 我是原型中的属性
    console.log('namer' in mc); // true

7.可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,有则返回true

    console.log(mc.hasOwnProperty('namer')); // false
    console.log(mc.hasOwnProperty('age')); // true

8.构造函数本身就是Function这个内置的构造函数的实例化对象

console.log(Person.__proto__ === Function.prototype); // true 

9.Function是自己的实例化对象

console.log(Function.prototype == Function.__proto__); // true

10.原型链:原型对象也是对象,所以它也有原型,当我们使用一个对象的属性或方法时,会先在自身中寻找,自身中如果有,则直接使用,如果没有则去原型对象中寻找,如果原型对象中有,则使用,如果还没有则去原型的原型中寻找,直到找到Object对象的原型,object对象的原型没有原型,如果在Object中依然没有找到,则返回undefined。

    console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); // true
    // console.log(mc.__proto__.__proto__.__proto__); // null
    console.log(mc.hello); // undefined

以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样每个对象都具有这些属性和方法了。

    function Person(namer, age, gender) {
      this.namer = namer;
      this.age = age;
      this.gender = gender;
      // this.sing = fn;
    }
    // 向Person原型对象中添加方法 所有创建的实例都可以访问
    Person.prototype.sing = function() {
      console.log('唱歌');     
    }
    var liudehua = new Person('刘德华', 12, '男');
    var zhangxueyou = new Person('张学友', 16, '男');
    var ljr = new Person('梁静茹', 23, '女');
    console.log(liudehua);  //  {namer: "刘德华", age: 12, gender: "男", sing: ƒ}
    console.log(zhangxueyou); // {namer: "张学友", age: 16, gender: "男", sing: ƒ}
    console.log(ljr); //  {namer: "梁静茹", age: 23, gender: "女", sing: ƒ}
    liudehua.sing(); // 唱歌 调用方法
    ljr.sing(); // 唱歌
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值