学习JavaScript第一弹(中)——ECMAScript(JavaScript基础)

5、作用域、作用域链、闭包

作用域

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。

JavaScript(es6前)中的作用域有两种:

  • 全局作用域
  • 局部作用域(函数作用域)
全局作用域
/**
 *  @函数全局作用域
 *  作用于所有代码执行的环境(整个 script 标签内部)或者一个独立的 js 文件。
 */
// 整个script标签内部
<sctipt type="text/script">
	...
</sctipt>
// 独立的js文件
(function fn(){
	...
})()
局部作用域
/**
 *  @局部作用域
 *  作用于函数内的代码环境,就是局部作用域。(跟函数有关,简称函数作用域)
 */
function fn(){
  // 函数作用域
  ...
}
块级作用域
/**
 *  @块级作用域
 *  块作用域由{}包括
 *  在其他编程语言中(如java、c#等),在if语句,循环语句中创建的变量,仅仅只能在本if语句,本循环语句中使用
 *  js中没有块级作用域(在ES6之前)
 */
if (true) {
  var num = 123;
  console.log(num); // 123
}
console.log(num); // 123
变量的作用域

全局变量

/**
 *  @全局变量
 *  在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)
 *  1、全局变量在代码的任何位置都可以使用
 *  2、在全局作用域下var声明的变量是全局变量
 *  3、特殊情况下,在函数内不使用var声明的变量也是全局变量(不建议使用)
 */
var num;
function fn() {
  num = 0;
  console.log(num); // 0
  count = 0;
}
console.log(count); // 0

局部变量

/**
 *  @局部变量
 *  在局部作用域下声明的变量叫作局部变量(在函数内部定义的变量)
 *  1、局部变量只能在该函数内部使用
 *  2、在函数内部var声明的变量是局部变量
 *  3、函数的形参实际上就是局部变量
 */

function fn(target){
  var num=0;
  console.log(num);
  target='haha'
  console.log(target);
}
console.log(num); // num is not defined
console.log(target);// target is not defined

全局变量和局部变量的区别

  • 全局变量:在任何一个地方都可以使用,只有在浏览器关闭时才会被销毁,因此比较占内存
  • 局部变量:只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后,就会被销毁,因此更节省内存空间
作用域链

只要是代码都一个作用域中,写在函数内部的局部作用域,未写在任何函数内部即在全局作用域中;

如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域;

根据在**[内部函数可以访问外部函数变量]**的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链

function f1() {
    var num = 123;
    function f2() {
        console.log( num );
    }
    f2();
}
var num = 456;
f1(); // 123

在这里插入图片描述

// 作用域链:采取就近原则的方式来查找变量最终的值。
var a = 1;
function fn1() {
    var a = 2;
    var b = '22';
    fn2();
    function fn2() {
        var a = 3;
        fn3();
        function fn3() {
            var a = 4;
            console.log(a); //a的值 ?
            console.log(b); //b的值 ?
        }
    }
}
fn1(); // a:4,b:22

在这里插入图片描述

闭包
/**
 *  @闭包
 *  闭包(closure)指有权访问另一个函数作用域中变量的函数。
 *  简而言之:一个作用域可以访问另外一个函数内部的局部变量。
 *  作用:延伸变量的作用范围。
 */
// 闭包函数
function fn1() {
  var num = 10;
  function fn2() {
    console.log(num); // 10
  }
  fn2();
}
fn1();
// 作用案例
function fn() {
  var num = 10;
  function fun() {
    console.log(num);
  }
  return fun;
}
var f = fn();
f();

案列:

/**
 *  1、得到当前li的索引号
 */
for (var i = 0; i < lis.length; i++) {
  (function (i) {
    lis[i].onclick = function () {
      console.log(i);
    };
  })(i);
}

/**
 *  2、3秒之后,打印所有li元素的内容
 */
for (let i = 0; i < lis.length; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(lis[i].innerHTML);
    }, 3000);
  })(i);
}

/**
 *  3、计算打车价格
 *  需求:打车起步价13(3公里内),  之后每多一公里增加 5块钱.  
 *        用户输入公里数就可以计算打车价格
 *        如果有拥堵情况,总价格多收取10块钱拥堵费
 */
var car = (function () {
  var start = 13; // 起步价  局部变量
  var total = 0; // 总价  局部变量
  return {
    // 正常的总价
    price: function (n) {
      if (n <= 3) {
        total = start;
      } else {
        total = start + (n - 3) * 5;
      }
      return total;
    },
    // 拥堵之后的费用
    yd: function (flag) {
      return flag ? total + 10 : total;
    },
  };
})();
console.log(car.price(5)); // 23
console.log(car.yd(true)); // 33

6、对象、构造函数、实例化

对象
创建对象
/**
 *  @对象
 *  对象是由属性和方法组成的:是一个无序键值对的集合,指的是一个具体的事物
 *  @属性:事物的特征,在对象中用属性来表示(常用名词)
 *  @方法:事物的行为,在对象中用方法来表示(常用动词)
 */
// 字面量创建对象
var starPerson={
  name:'张三',
  age:18
}

// 构造函数创建
function Star(name,age){
  this.name=name;
  this.age=age;
}

var lisi=new Star('李四',20); // 实例化对象
console.log(lisi);

// 类创建对象(ES6)
class Star{
  constructor(name,age){
    this.name=name;
    this.age=age;
  }
}

var zs=new Star('张三',22);
console.log(zs);
  1. 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写
  2. 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
  3. constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
  4. 多个函数方法之间不需要添加逗号分隔
  5. 生成实例 new 不能省略
  6. 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function
构造函数
/**
 *  @构造函数
 *  定义:通过 new 函数名,来实例化对象的函数叫构造函数。
 *  任何函数都可以作为构造函数存在。
 *  构造函数定义时首字母大写(规范)
 *  对new的理解:
 *  1、new申请内存,创建对象;
 *  2、当调用new时,后台会隐式执行new Object()创建对象。
 *  通过new创建的字符串、数字时引用类型,而是非值类型。
 */
// 常用构造函数
var arr=[]; /* 语法糖 */ var arr=new Array();
var obj={};/* 语法糖 */ var obj=new Object();
var date=new Date();
实例化
/**
 *  @实例化
 *  静态的成员变量和方法,都是不需要进行实例化类的。可以直接调用。
 *  非静态的成员变量和方法,都要进行实例化类的。才可以调用
 */
// 工厂模式
function obj() {
  var person = new Objec(); //创建对象,对象属性赋值
  person.name = "zs";
  person.sex = "男";
  person.hobby = function () {
    console.log("打篮球");
  };
  return zs;
}

var zs = obj();
console.log(zs.name, zs.sex);
zs.hobby();

// 构造函数模式
function obj2(name, age) {
  this.name = name;
  this.age = age;
  this.hobby = function () {
    console.log("跑步");
  };
}

var lisi = new obj2("李斯", 22);
console.log(lisi.name, lisi.age);
lisi.hobby();

// 原型模式
function obj3() {}
obj3.prototype.name = "王五";
obj3.prototype.love = function (name) {
  console.log("喜欢" + name);
};

var wangwu = new obj3();
/* 检测实在实例中还是在原型上 */
console.log(wangwu.hasOwnProperty("name"));
console.log();
wangwu.love("三妹");

// 混合模式
function obj4(age) {
  this.age = age;
  this.rename = "aaa";
}
obj3.prototype = {
  constructor: obj4,
  name: "赵六",
  age: 23,
  love: function (name) {
    console.log(name + "看书");
  },
};

var zl=new obj4(20);
console.log(zl.hasOwnProperty('age'));
zl.love('zhaoliu');

7、原型、原型链、对象继承

原型

构造函数原型

/**
 *  @构造函数原型
 *  构造函数通过原型分配的函数时所有对象共享的。
 *  每一个构造函数都有一个prototype属性,指向另一个对象。
 *  注:prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
 *  可以把哪些不变的方法,直接定义在prototype对象上,所有对象的实例就可以共享。
 */
function Star(name,age){
  this.name=name;
  this.age=age;
}

Star.prototype.sing=function(){
  console.log('会唱歌');
}
var ldh=new Star('刘德华',55);
var zxy=new Star('张学友',50);
ldh.sing();
zxy.sing();

在这里插入图片描述

对象原型

/**
 *  @对象原型
 *  对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,
 *  之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。
 *  __proto__对象原型和原型对象 prototype 是等价的。
 *  __proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
 * 
 */
function Star(uname, age) {
     this.uname = uname;
     this.age = age;
 }
 // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
 Star.prototype = {
 // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
   constructor: Star, // 手动设置指回原来的构造函数
   sing: function() {
     console.log('我会唱歌');
   },
   movie: function() {
     console.log('我会演电影');
   }
}
var zxy = new Star('张学友', 19);
console.log(zxy)
原型链
/**
 *  @原型链
 *  每一个实例对象有一个proto对象,指向的构造函数的原型对象。
 *  构造函数的原型对象也是一个对象,也有proto属性,一层层往上找就形成了原型链
 */

在这里插入图片描述

构造函数和原型对象三角关系

  1. 构造函数的prototype属性指向了构造函数原型对象

  2. 实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象

  3. 构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数

在这里插入图片描述

对象继承
类的继承
/**
 *  类的继承
 */
class Father {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log("姓名:" + this.name + ", 年龄:" + this.age);
  }
}

class Son extends Father {
  constructor(name, age, hobby) {
    super(name, age);
    this.hobby = hobby;
  }
  hobby(){
    console.log('爱好:'+this.hobby);
  }
}

var son = new Son("zs", 18,'篮球');
son.say(); // 姓名:zs,年龄:18
son.hobby(); // 爱好:篮球
  1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的

  2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

  3. 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用

8、对象、对象克隆

对象
/**
 *  @对象
 *  第七种数据类型,唯一一种复杂类型
 *  1、数据集合,无序的
 *  2、键值对的集合(键与值组成一对称为键值对)
 *  3、每个value都时对象的属性值
 *  4、所有属性名会自动转变成字符串
 *  5、js中每一个对象都有一个隐藏属性,存储着其共有属性组成的对象的地址;这个共有属性组成的对象叫做原型,隐藏属性存储着原型的地址
 */
//  1.查看自身所有属性
 Object.keys(obj)
//  2.查看自身所有值:
 Object.values(obj)
//  3.查看自身所有属性和值:
 Object.entries(obj)
//  4.查看自身+共有属性
 console.dir(obj) 
//  5.判断一个属性是自身的还是共有的
 obj.hasOwnProperty('toString')
对象克隆

1.通用对象克隆

/**
 *  @通用对象克隆
 */
function clone(obj) {
  let temp = null;
  if (obj instanceof Array) {
    temp = obj.concat();
  } else if (obj instanceof Function) {
    // 函数是共享的
    temp = obj;
  } else {
    temp = new Object();
    for (let item in obj) {
      let val = obj[item];
      // 判断是否为函数,是函数当作一般值处理
      temp[item] = typeof val == "object" ? clone(val) : val;
    }
  }
  return temp;
}

2.JSON对象序列化

/**
 *  @JSON对象序列化
 *  弊端:不能赋值函数
 */

var obj={}
var newObj=JSON.parse(JSON.stringify(obj));

3.DOM元素复制cloneNode

/**
 *  @DOM元素复制
 */
let div=document.getElementById('box');
let box2=div.cloneNode(true);

4.ES6新方法——Object.assign

/**
 *  @ES6方法
 */
var obj={};
var newObj=Object.assign({},obj);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值