PHP 学习之路:第十天—— 闭包/循环/构造器与原型

一、作用域与闭包

1. 作用域

1.1 全局作用域

(1) 全局作用域在页面打开时被创建,页面关闭时被销毁
(2) 写在 script 标签中的变量和函数,作用域为全局,在页面的任意位置都可以访问到
(3) 在全局作用域中有全局对象 window,代表一个浏览器窗口,由浏览器创建,可以直接调用
(4) 全局作用域中声明的变量和函数会作为 window 对象的属性和方法保存

// 1. 全局作用域,默认的,不可删除
ver name = 'php中文网';
console.log(name);
// 由全局对象调用的
// 全局对象: 如果在是浏览器中运行js,那么全局对象就是window
console.log(window.name);
复制代码

1.2 函数作用域

(1) 调用函数时,函数作用域被创建,函数执行完毕,函数作用域被销毁
(2) 每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
(3) 在函数作用域中可以访问到全局作用域的变量,在函数外无法访问到函数作用域内的变量
(4) 在函数作用域中访问变量、函数时,会先在自身作用域中寻找,若没有找到,则会到函数的上一级作用域中寻找,一直到全局作用域
(5) 在函数作用域中也有声明提前的特性,对于变量和函数都起作用,此时函数作用域相当于一个小的全局作用域
(6) 在函数作用域中,不使用变量关键字声明的变量,在赋值时会往上一级作用域寻找已经声明的同名变量,直到全局作用域时还没找到,则会成为 window 的属性
(7) 在函数中定义形参,等同于声明变量

let site = 'php中文网';
function getSite() {
  // site是声明在函数外部的全局变量
  // 在函数内部可以访问到外部的全局变量
  // let site = "京东商城";
  
  // 私有成员,仅限在当前作用内访问, 外部不可见
  let domain = 'php.cn';
  return `${site} [ ${domain} ]`;
  // 这里要返回一个叫site的变量
  // 有一个查询的过程, 先在自身的作用域找一个有没有一个叫site
  // 如果有则直接返回它
  // 如果函数中没有这个site,则自动函数的上一级作用域中去查看site
  // 全局正好有一个site,于是就返回了全局的site
  // 内部的site ---> 到它的上一级作用域中去查找
  // 上面的查询变量的过程,就是是一个链式查询的一个过程,称为:作用域链
}
console.log(getSite());
复制代码

1.3 块作用域

(1)用{}创建快作用域
(2) let, const 支持块作用域; var 不支持块作用域

{
  let a = 1;
  const B = 'hello';
  // var:不支持块作用域
  var c = 1;
}
console.log(a, B, c);
复制代码

2. 闭包

闭包:能够访问自由变量的函数;理论上讲,所有函数都是闭包

let c = 100;
function sum(a, b) {
  // 自由变量,函数参数以外的变量;c 为自由变量
  return a + b + c;
}
console.log(sum(4, 5));

// 通过闭包来访问内部的私有变量;
function demo1() {
  // 私有变量
  let email = 'a@qq.com';
  return function d() {
    // 对于这个子函数来说,email就是它的自由变量
    return email;
  };
}
console.log(demo1()());
复制代码

二、循环

1. while: 入口判断

const colors = ["red", "green", "blue"];
// 1. while: 入口判断
//   循环变量的初始化
let i = 0;
//   i < colors.length: 循环条件
while (i < colors.length) {
  // console.log(colors[i]);
  console.log("%c%s", "color: red", colors[i]);
  // 更新循环条件,如果不更新就进入了死循环
  // i = i + 1;
  // i += 1;
  // 自增1
  i++;
  // i+=2;
}
复制代码

2. while: 出口判断

const colors = ["red", "green", "blue"];
// 2. while: 出口判断
//   当条件不满足的时候至少执行一次
//   循环变量的初始化
i = 10;
//   i < colors.length: 循环条件
do {
  console.log("%c%s", "color: blue", colors[i]);
  console.log("我被执行了一次");
  // 更新循环条件,如果不更新就进入了死循环
  i++;
} while (i < colors.length);
复制代码

3. 对象的遍历 for-in

// 3. 对象的遍历, for -in
const lesson = { "my name": "JavaScript编程", score: 88 };
console.log(lesson);

for (let key in lesson) {
  console.log(lesson[key]);
}
复制代码

4. for 循环

// 4. for 循环
const colors = ["red", "green", "blue"];
// 循环变量的初始化
i = 0;
// i < colors.length: 循环条件
while (i < colors.length) {
  // console.log(colors[i]);
  console.log("%c%s", "color: red", colors[i]);
  // 更新循环条件,如果不更新就进入了死循环
  // i = i + 1;
  // i += 1;
  // 自增1
  i++;
  // i+=2;
}
// while的简化版
for (i = 0; i < colors.length; i++) {
  console.log("%c%s", "color: green", colors[i]);
}
复制代码

5.迭代器 for-of,遍历数组

// 迭代器,将所有类型的数据的遍历进行了统一操作
// for - of
// 遍历数组
const colors = ["red", "green", "blue"];
for (let item of colors) {
  console.log(item);
}
// 遍历对象,会报错
const lesson = { "my name": "JavaScript编程", score: 88 };
for (let item of lesson) {
  console.log(item);
}
复制代码

三、迭代器

function myIterator(data) {
  // 迭代器中必须要有一个next()
  let i = 0;
  return {
    next() {
      // done: 表示遍历是否完成?
      // value: 当前正在遍历的数据
      let done = i >= data.length;
      let value = !done ? data[i++] : undefined;
      return { done, value };
    },
  };
}

let iterator = myIterator(["html", "css", "js", "php"]);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
复制代码

四、构造函数和原型

// 常识: 任何一个函数都是对象,有一个属性叫: prototype(原型属性)
function f1(a, b, c) {}
console.dir(f1);

// 函数有二个功能
// 基本功能是封装操作步骤
// 扩展功能: 当成对象的构造器,构造函数,对象生成器来用
// 在js中没有“类”的概念,都是通过原型来实现继承的
// 为了区别函数的这二个功能,当一个函数当成构造函数来使用时,必须使用“new"来调用

// 通过构造函数创建对象的过程,叫”类的实例化"
// 此时,构造函数就可以看成一个类

function User(name, email) {
  // 1.内部会自动创建一个this,指向新生成的对象
  // let this = new User();

  // 2.第二步是给这个新生成的对象添加一些成员(属性,方法)
  this.name = name;
  this.email = email;

  // 3.返回这个新对象
  // return this;
}

// 当成普通函数调用
// const user = User();

// 当成构造函数调用 new
const user = new User("admin", "admin@php.cn");
console.log(user, typeof user);

// user对象的原型属性永远指向它的构造函数的原型属性对象
// user对象的原型
console.log(user.__proto__);
// user的构造函数的原型
console.log(User.prototype);
console.log(user.__proto__ === User.prototype);
复制代码

五、类与类的继承

1.类: 使用 class 关键字

class User1 {
  // 构造方法:初始化对象的
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  // 原型方法(共享方法),通过对象来调用的
  show() {
    return { name: this.name, email: this.email, age: this.#age };
  }

  // 静态方法: 不需要实例化(new class),直接用类来调用
  static fetch() {
    // 静态成员中的this表示的就是当前的类
    return this.userName;
  }
  // 静态属性/变量
  static userName = '灭绝小师妹';

  // 私有成员
  #age = 50;

  // 还可以声明访问器属性: 伪装成属性的方法,有get,set
  // 使用访问器属性来访问私有成员
  get age() {
    return this.#age;
  }

  set age(value) {
    if (value >= 18 && value < 60) this.#age = value;
    else console.log('年龄非法');
  }
}

const user1 = new User1('天蓬老师', 'tp@qq.com');
console.log(user1.show());

//   静态方法直接用类调用
console.log(User1.fetch());

console.log('my age = ', user1.age);
user1.age = 160;
console.log('my age = ', user1.age);
user1.age = 32;
console.log('my age = ', user1.age);
复制代码

2.继承:使用 extends 关键字

// 继承,通常是对父类进行一些扩展(添加一些新的属性或方法)
class Child extends User1 {
  constructor(name, email, gender) {
    // 第一步必须将父类的构造方法来执行一下,否则this用不了
    super(name, email);
    // 第二步给子类的新成中去初始化
    this.gender = gender;
  }
  // 父类的原型方法
  show() {
    return { name: this.name, email: this.email, gender: this.gender };
  }
}

const child = new Child('欧阳老师', 'oy@qq.com', '男');
console.log(child.show());
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享