阅读 1208

JS基础难点总结(函数作用域,变量提升,对象,构造函数,this)全!!!

JavaScript的组成

ECMAScrpit - 描述了语言的基本语法和数据类型  
BOM - 浏览器对象模型:一套操作浏览器功能的API,可以通过BOM弹框,获取屏幕分辨率  
DOM - 文档对象模型:一套操作页面元素的API,可以操作文档树上的节点
复制代码

变量

什么是变量

变量是计算机内存中存储数据的标识符,根据变量名可以获取内存中存储的数据   
使用变量可以方便的获取或修改内存中的数据
复制代码

如何使用变量

用var来声明
var a = 'monster'
注意:js中的变量如果不用var来声明,如: b = 'monster' ,b就是全局变量(全局对象属性)

var a = b = 'monster'
在预编译时会被解析为 
var a;
a = 'monster';
b = 'monster'
复制代码

这时b就是一个全局变量,但是不推荐使用

函数

实参和形参

// 函数内部是一个封闭的环境,可以通过参数的方式,把外部的值传递给函数内部
// 带参数的函数声明
function 函数名(形参1, 形参2, 形参3...) {
  // 函数体
}
// 带参数的函数调用
函数名(实参1, 实参2, 实参3); 
形参1 = 实参1
形参2 = 实参2
复制代码
var x = 5, y = 6;
fn(x,y); 
function fn(a, b) {
  console.log(a + b);
}
//x,y是实参,函数执行的时候会把x,y复制给a,b;函数内部是复制后的新值,无法改变外部的x,y
复制代码

函数的返回值

1.//声明一个带返回值的函数
function 函数名(形参1, 形参2, 形参3...) {
  //函数体
  return 返回值;
}
2.//用变量接受这个返回值
var 变量 = 函数名(形参1, 形参2, 形参3...)
函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。
复制代码

arguments

函数内部 arguments 对象:可以通过arguments获取函数的实参。 例:可以用来求 任意数字的和

1. 可以通过arguments获取函数调用时传入的实参
function test(a,b,c) {
    console.log(arguments)
}
2. 求任意个数的最大值
function getMax() {
    max = arguments[0];
    for(var i = 0; i < arguments.length; i++) {
        if(max < arguments[i]) {
            max = arguments[i];
        }
    }
    return max;
}

var max = getMax(2,10,4,67,29)   //传入不定个数的数组
console.log(max)
复制代码

函数是一种数据类型

function fn() {}
console.log(typeof fn);
复制代码
  • 函数作为参数
    因为函数也是一种类型,可以把函数作为两一个函数的参数,在另一个函数中调用
  • 函数作为返回值
    因为函数是一种类型,所以可以把函数作为返回值从函数内部返回
function fn(b) {
    var a = 10;
    return function() {
        alert(a + b);
    }
}
fn(15)();
复制代码

作用域

作用域:变量可以起作用的范围
复制代码

全局变量和局部变量

 全局变量

 在任何地方都可以访问到的变量就是全局变量,对应全局作用域

 局部变量

 只在固定的代码片段内可访问到的变量,最常见的例如函数内部。对应局部作用域(函数作用域)
复制代码

变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁

function fn() {
    var num = 5;
}
console.log(num)   //num is not defined
//函数内部定义的变量num是局部变量,在外部不可以访问
复制代码

作用域链

只有函数可以制造作用域结构,那么只要是代码,就至少有一个作用域,即全局作用域。
凡是代码中有函数,那么这个函数就构成另一个作用域。
如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。
复制代码

将这样的所有的作用域列出来形成作用域链 : 函数内指向函数外的链式结构。代码在运行时,先在函数内部找变量,找不到再去函数外找

 // 全局作用域 -- 0级作用域链
var num = 5;
function f1() {
  // f1 局部作用域   -- 1级作用域链
  // var num = 15;
  function f2() { 
    // f2 局部作用域  -- 2级作用域链
    console.log(num);
  }
  f2();
}
f1();
复制代码

预解析 -- 变量提升

预解析过程:

  1. 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值
  2. 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用
  3. 先提升var,再提升function
  4. 提升完后其他代码位置不变
1var a = 25;
    function abc() {
        alert(a); 
        var a = 10;
    }
    abc();
    
   预解析后如下:
    var a;
    function abc() {
        var a;
        alert(a);
        a = 10;
    }
    a = 25;
    abc();  //ndefined;
复制代码
2:
    var a = 18;
    f1();
    function f1() {
      var b = 9;
      console.log(a);
      console.log(b);
      var a = '123';
    }
    
   预解析后如下:
    var a;
    function f1() {
        var b;
        var a;
        b = 9;
        console.log(a);
        console.log(b);
        a = '123';
    }
    a = 18;
    f1();
    // undefined 
    // 9
复制代码

对象

为什么要有对象

function printPerson(name, age, sex....) {
}
// 函数参数很多的化,可以传入对象简化
function printPerson(person) {
    console.log(person.name)
}
复制代码

什么是对象

现实生活中,万物皆对象,比如一辆车,一部手机。
对象是一个具体的事物,事物就会有特征和行为。
js中的对象是生活中对象的抽象,可以把JavaScript中的对象想象成键值对,其中值可以是数据(对象字面量)和函数。
对象的行为和特征:
    特征---属性
    行为---方法

思考:狗是对象吗?我认为狗不是对象,狗描述了一类事物,但我家的萨摩耶是一个对象
复制代码
对象和函数有相似性,函数封装了代码,可重用;对象封装了一组属性和方法,同样可重用。
复制代码

对象创建方式

对象字面量

创建小明对象,小明具有的 属性有  name,age等,方法有 打招呼
复制代码
var o = {
  name: 'zs',
  age: 18,
  sex: true,
  //在方法中如何使用对象的属性呢?用this.
  //this代表的是当前对象
  sayHi: function () {
    console.log(this.name + '你好');
  }
  }

//访问对象的属性    console.log(o.name) 或 console.log(o['name'])
//访问对象的方法    o.saiHi()
复制代码
思考:函数和方法的区别:方法是对象的函数;通过 o.sayHi()调用
复制代码

new Object()

通过new来创建构造函数

var person = new Object();   //new Object()调用构造函数;在内存中创建了一个空的对象,
然后可以通过动态的方式给对象添加属性和方法。体现了js的动态性
person.name = 'monster'
person.sayHi = function() {
console.log('Hello,everyBody');
}
复制代码

工厂模式批量创建对象

function createPerson(name,age,job) {
    var person = new Object();
    person.name = name;
    person.age = age;
    person.job = job;
    person.sayHi = function(){
        console.log('Hello,everyBody');
  };
  return person;
}
var p1 = createPerson('monster',23,'cleaner')
复制代码

自定义构造函数(推荐)

function Person(name,age,job) {
    this.name = name;       //this指向当前对象,this.name动态的为对象添加name 属性,并且使name的值等于传过来参数的值
    //注意形参的名字要和后面的name一样;比如传过来的形参是a,那么this.name = a
    this.age = age;
    this.job = job;
    this.sayHi = function(){
  	console.log('Hello,everyBody');
  }
}
var p1 = new Person('monster',23,'cleaner')
复制代码
注意:
1. 构造函数用于创建一类对象,首字母要大写。

还记得狗狗这一类吧,构造函数相当于创建了“狗”类,然后随便你要什么狗,
哈士奇,泰迪,直接new Dog('二哈','白色') 创建狗这个对象。。
2. 构造函数要和new一起使用才有意义。
复制代码
new在执行时会做四件事情:

new会在内存中创建一个新的空对象
new 会让this指向这个新的对象
执行构造函数  目的:给这个新对象加属性和方法
new会返回这个新对象
复制代码

this

this的指向问题还是挺让人头疼的,实验结果如下

1.函数中的this  -----> 指向window
 function fn() {
     console.log(this);
 }
 fn()
 
2.方法中的this  -----> 指向方法所属的对象
var obj = {
    name : 'monster',  //注意逗号结尾
    fn : function() {
        console.log(this)
    }
}
obj.fn()

3.构造函数中的this  ----->   指向构造函数创建的对象
function Fn() {
this.name = 'monster'
    console.log(this)
}
var o = new Fn()  //调用构造函数
如果通过 Fn() 调用, 结果是指向window,以为是调用函数
复制代码
总结:
1. 函数在定义的时候this是不确定的,只有在调用的时候才可以确定
2. 一般函数直接执行,内部this指向全局window
3. 函数作为一个对象的方法,被该对象所调用,那么this指向的是该对象
4. 构造函数中的this其实是一个隐式对象,类似一个初始化的模型,所有方法和属性都挂载到了这个隐式对象身上,后续通过new关键字来调用,从而实现实例化
复制代码

遍历和删除对象属性

遍历

for in 可以遍历对象的属性成员

var obj = {
  name: 'zs',
  age: 18,
  sex: true,
  sayHi: function () {
    console.log(this.name + '你好');
  }
  }
  for (var key in obj)  //key 是对象的属性成员
  { 
    console.log(key + '---' + obj[key]) ;
  }
  //答应对象的属性和对应的值
复制代码
可以通过循环的方式动态给对象增加属性

var o = {};
for (var i = 0; i < 10; i++) {
    o[a + 'i'] = i * 2;
}
复制代码

删除

通过delete删除


function Fun() {
    this.name = 'monster'
}
var obj = new Fun()
console.log(obj.name); // monster
delete obj.name
console.log(obj.name); // undefined


复制代码

总结完这些不知不觉已经快深夜2点了~~
如果对您有帮助,就麻烦点个赞吧,欢迎交流

文章分类
前端
文章标签