0%

学习笔记 2020 10 25

学习笔记 2020-10-25

JavaScript 高级程序设计(第4版) 阅读记录

代理与反射

代理模式

数据绑定与可观察对象

通过代理可以实现把被代理的类绑定到一个全局实例集合,让所有创建的实例都添加到这个集合中。再把集合绑定到一个事件分派程序,每次插入新实例时都会发送消息。

函数

函数实际上是个对象。每个函数都是 Function 类型的实例。

定义函数的方法有:

  • 函数声明

  • 函数表达式

  • 箭头函数

  • Function 构造函数

    该构造函数接收任意多个字符串参数,最后一个参数始终会被当成函数体。

箭头函数

箭头函数语法简洁,但不能使用 argumentssupernew.target ,也不能用作构造函数。也没有 prototype 属性。

函数名

函数名就是指向函数的指针,与其他包含对象指针的变量具有相同的行为。一个函数可以有多个名称。使用不带括号的函数名会访问函数指针。

ECMAScript6 的所有函数对象都会暴露一个只读的 name 属性。如果函数是一个获取函数、设置函数或是使用 bind() 实例化,标识符前面会加上一个前缀。

function foo() {}
let bar = function () {};
let baz = () => {};
console.log(foo.name); // foo
console.log(bar.name); // bar
console.log(baz.name); // baz
console.log((() => {}).name); //(空字符串)
console.log(new Function().name); // anonymous

function foo() {}
console.log(foo.bind(null).name); // bound foo
let dog = {
  years: 1,
  get age() {
    return this.years;
  },
  set age(newAge) {
    this.years = newAge;
  }
};
let propertyDescriptor = Object.getOwnPropertyDescriptor(dog, 'age');
console.log(propertyDescriptor.get.name); // get age
console.log(propertyDescriptor.set.name); // set age

理解参数

ECMAScript 函数的参数跟大多数其他语言不同,不关心传入参数的个数、类型。

原因是 ECMAScript 函数的参数在内部表现为一个数组。函数并不关心这个数组中包含什么。在使用 function 关键字定义非箭头函数时,可以在函数内部使用 arguments 对象来访问每个参数。

arguments 是一个类数组对象,可以和命名参数一起使用。它的值始终会和对应的命名参数同步。两者是相对应的,但仅限于传入的参数对应。未传入的参数位置使用 arguments 修改是不会反映到命名参数上的。

function doAdd(num1, num2) {
  console.log(arguments); // Arguments [99, length: 1]
  console.log(num1, num2); // 99 undefined
  arguments[0] = 5;
  arguments[1] = 10;
  console.log(arguments); // Arguments [5, 1: 10, length: 1]
  console.log(num1, num2); // 5 undefined
  num1 = 1;
  num2 = 2;
  console.log(arguments); // Arguments [1, 1: 10, length: 1]
  console.log(num1, num2); // 1 2
}

doAdd(99);

在严格模式下给 aguments[1] 赋值不会再影响 num2 。在函数中重写 arguments 对象会导致语法错误。

没有重载

ECMAScript 中后定义的同名函数会覆盖先定义的。

默认参数值

ECMAScript5.1 及以前,实现默认参数的常用方式是检查某个参数是否等于 undefined

ECMAScript6 之后支持显式定义默认参数。在函数定义中的参数后面用 = 赋默认值。

在使用默认参数时, arguments 对象的值不反映参数的默认值,只反映传给函数的参数。修改命名参数也不会影响 arguments 对象。

默认参数值不限于原始值或对象类型,也可以使用调用函数返回的值。

默认参数只有在函数被调用时才会求值,定义时不会求值。计算默认值的函数只有在调用函数但未传相应参数时才会被调用。

函数参数是某个作用域中求值的。给多个参数定义默认值实际上和使用 let 关键字顺序声明变量一样,所以后定义默认值的参数可以引用先定义的参数。

参数初始化顺序遵循 “暂时性死区” 规则。前面定义的参数不能引用后面定义的。

参数存在于自己的作用域中,不能饮用函数体的作用域。

现代 JavaScript 教程

任务

  1. 在对象字面量中使用 “this"

    这里 makeUser 函数返回了一个对象。

    访问 ref 的结果是什么?为什么?

    function makeUser() {
      return {
        name: "John",
        ref: this
      };
    };
    let user = makeUser();
    alert( user.ref.name ); // 结果是什么?

    ref 会指向全局对象,非严格模式下是 window 。因为 this 是在被调用的那一刻确定。调用 makeUser 时作为全局函数调用, this 绑定了 window

  2. 创建一个计算器

    创建一个有三个方法的 calculator 对象:

    • read() 提示输入两个值,并将其保存为对象属性。
    • sum() 返回保存的值的和。
    • mul() 将保存的值相乘并返回计算结果。
    let calculator = {
      // ……你的代码……
    };
    calculator.read();
    alert( calculator.sum() );
    alert( calculator.mul() );
    let calculator = {
      a: 0,
      b: 0,
      read: function() {
        this.a = +prompt('please enter the first number', 0);
        this.b = +prompt('please enter the second number', 0);
      },
      sum: function() {
        return this.a + this.b;
      },
      mul: function() {
        return this.a * this.b;
      }
    };
  3. 链式(调用)

    有一个可以上下移动的 ladder 对象:

    let ladder = {
      step: 0,
      up() {
        this.step++;
      },
      down() {
        this.step--;
      },
      showStep: function() { // 显示当前的 step
        alert( this.step );
      }
    };

    现在,如果我们要按顺序执行几次调用,可以这样做:

    ladder.up();
    ladder.up();
    ladder.down();
    ladder.showStep(); // 1

    修改 updownshowStep 的代码,让调用可以链接,就像这样:

    ladder.up().up().down().showStep(); // 1

    这种方法在 JavaScript 库中被广泛使用。

    let ladder = {
      step: 0,
      up() {
        this.step++;
        return this;
      },
      down() {
        this.step--;
        return this;
      },
      showStep: function() { // 显示当前的 step
        alert( this.step );
        return this;
      }
    };

MDN学习记录

多列布局

使用 column-gap 改变列间隙。

使用 column-rule 在列间加入一条分割线。属性值类似于 border 的值。并且该分割线不占用宽度。

列与内容折断

break-inside 属性用于表示该盒子不被拆开。

page-break-inside 拥有更好的浏览器支持。

响应式设计

响应式网页设计 ( responsive web design ),指的是允许 Web 网页适应不同屏幕宽度因素等,进行布局和外观的调整的一系列实践。

  1. 液态网格,这早先已由Gillenwater进行探讨,可以在Marcotte的文章《Fluid Grids》(出版于2009年的《A List Apart》上)中读到。
  2. 液态图像。通过使用相当简单的将设置 max-width 属性设置为 100% 的技术,图像可以在包含它们的列变得比图像原始尺寸窄的时候,缩放得更小,但总不会变得更大。这使得图像可以被缩放,以被放到一个灵活尺寸的列,而不是溢出出去,同时也不会在列宽于图像的时候,使图像变得太大以至于画质变得粗糙。
  3. 媒体查询,媒体查询使以往 Cameron Adams 探讨过的、由 JavaScript 实现的布局类型切换,可以只使用CSS实现。和所有尺寸的屏幕都使用一种布局不同的是,布局是可以改变的:侧栏可以在小屏幕上重新布局,而替代用的导航栏也可以显示出来。

媒介查询

下面的媒体查询进行测试,以知晓当前的Web页面是否被展示为屏幕媒体(也就是说不是印刷文档),且视口至少有800像素宽。用于 .container 选择器的CSS将只会在这两件前提存在的情况下应用。

@media screen and (min-width: 800px) {
  .container {
    margin: 1em 2em;
  }
}

媒体查询,以及样式改变时的点,被叫做断点。

媒体查询

媒体查询基础

最简单的媒体查询语法看起来是像这样的:

@media media-type and (media-feature-rule) {
  /* CSS rules go here */
}

它由以下部分组成:

  • 一个媒体类型,告诉浏览器这段代码是用在什么类型的媒体上的(例如印刷品或者屏幕);
  • 一个媒体表达式,是一个被包含的CSS生效所需的规则或者测试;
  • 一组CSS规则,会在测试通过且媒体类型正确的时候应用。

可指定的媒体类型为:

  • all
  • print
  • screen
  • speech

朝向

一个受到良好支持的媒体特征是 orientation,我们可以用它测得竖放(portrait mode)和横放(landscape mode)模式。要在设备处于横向的时候改变body文本颜色的话,可使用下面的媒体查询。

@media (orientation: landscape) {
  body {
    color: rebeccapurple;
  }
}

使用指点设备

作为四级规范的一部分,hover 媒体特征被引入了进来。这种特征意味着你可以测试用户是否能在一个元素上悬浮,这也基本就是说他们正在使用某种指点设备,因为触摸屏和键盘导航是没法实现悬浮的。

@media (hover: hover) {
  body {
    color: rebeccapurple;
  }
}

还是在四级规范中,出现了 pointer 媒体特征。它可取三个值:nonefinecoarsefine 指针是类似于鼠标或者触控板的东西,它让用户可以精确指向一片小区域。coarse 指针是你在触摸屏上的手指。none 值意味着,用户没有指点设备,也许是他们正只使用键盘导航,或者是语音命令。

使用 pointer 可以在用户使用屏幕时进行交互时,帮你更好地设计响应这种交互的界面。例如,如果你知道用户正在用触摸屏设备交互的时候,你可以建立更大的响应区域。

媒体查询逻辑判断

  • and
  • ,
  • not

特性查询

可以通过特性查询来判断浏览器是否支持某个特性。

@supports (display: grid) {
  .item {
    width: auto;
  }
}