Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES6 - Part 1 #24

Open
ONE-SUNDAY opened this issue Jul 19, 2017 · 1 comment
Open

ES6 - Part 1 #24

ONE-SUNDAY opened this issue Jul 19, 2017 · 1 comment

Comments

@ONE-SUNDAY
Copy link
Owner

ONE-SUNDAY commented Jul 19, 2017

一、let、const 声明变量

  • var 是允许重新赋值、重新定义的,只有 globalfunction scope 作用域,在 if 内无作用域

  • letconst 拥有 block scope 作用域,有 {} 的时候就表明它的作用域范围

  • letconst 不可重复声明一个变量,所以使用 var 容易不小心覆盖原来的变量

  • let 可以重新赋值,const 不可以重新赋值

  • const 也是可以修改的,不过要是引用类型,相当于人(Person)是不可改变的,但我的年龄会随着时间改变

const person = {
  name: 'Jelly',
  age: 20
}

person.age = 21;

实在不希望改变,可以通过 Object.freeze

阻止修改现有属性的特性和值,并阻止添加新属性。

const person = {
  name: 'Jelly',
  age: 20
}

const Jelly = Object.freeze(person);

person.age = 21; // 无法修改,返回值依旧是 20

应用场景

1、通过 letconst 实现私有变量

window 对象下有一个 name 属性

window.name = 'Jelly'; // 这样就影响到 window 下的 name 属性

为了保证我们不影响到 window 下的 name 属性,我们通过 IIFE(立即执行函数)去实现私有化变量,但为了这个目的而去写这个函数,不利于代码的可读性

(function() {
  var name = 'Jelly';
})();

window.name // ""

有了 letconst 就可以通过它们的特性 block scope 来实现了

{
  const name = 'Jelly';
}

window.name // ""

2、for 循环应用场景

for (var i = 0; i < 10; i++) { // 每次循环都重写了 i 的值
  setTimeout(function() { // 模拟 ajax 请求
    console.log(`i:${i}`); // i:10(10个重复)
  }, 1000);
}

console.log(window.i); // 10

上面返回 10个 i:10 的原因就在于,在 setTimeout 1秒后执行的时候,循环已经结束

只需将 var 改为 let 声明,每次声明的都属于当前的块级作用域,setTimeout 只认这个作用域

for (var i = 0; i < 10; i++) { // 每次循环都重写了 i 的值
  setTimeout(function() { // 模拟 ajax 请求
    console.log(`i:${i}`); // 输出 i:0 到 i:9
  }, 1000);
}

console.log(window.i); // 10

PS:const 不能使用,const 的值是不能重新赋值的

3、临时性死区(Temporal dead zone)

console.log(color);
var color = 'yellow';

console.log(color); // undefined

原因是变量提升(是 JS 将声明移到作用域的顶部)

相当于是:

var color;
console.log(color);
color = 'yellow';

使用 letconst 呢?

console.log(color);  // ReferenceError
let color = 'yellow';

同样有变量提升,它们会存在于临时性死区,所以这个特征让我们养成在变量未声明的时候,不要使用

二、箭头函数(arrow function)

三个特点:

  • 简明的语法
  • 隐式返回
  • 不绑定 this

简明的语法

// 传统写法
const numbers = [5, 6, 13, 5];

const double = numbers.map(function(number) {
  return number * 2;
})

console.log(double); 
// 简化
const numbers = [5, 6, 13, 5];

const double = numbers.map(number => { // 一个参数不需要括号,多个参数需要,没有参数需要保留括号
  return number * 2;
})

console.log(double); 

隐式返回

有 return 则表示是显示返回

const numbers = [5, 6, 13, 5];

const double = numbers.map(number => number * 2); // 放在一行,在我们需要简单返回一个东西的时候特别有用

console.log(double); 

不绑定 this

const Jelly = {
  name: 'Jelly',
  hobbies: ['Coding', 'Reading', 'Sleeping'],
  printHobbies: function() {
    console.log(this); // printHobbies 的 this 指向调用它的对象 Jelly
    this.hobbies.map(function(hobby) { // map 函数是独立的函数,独立运行时候,没通过 call、apply、bind 来改变 this 的情况下,这个 this 则指向 window、global 或者在严格模式下指向 undefined
      console.log(`${this.name} loves ${hobby}`);
    });
  }
}

Jelly.printHobbies(); // 打印不对,this.name 是空

老方法是通过:var self = this

新方法则通过箭头函数,箭头没有自己的 thisthis 是继承父级的

this.hobbies.map(hobby => {
  console.log(`${this.name} loves ${hobby}`);
});

箭头函数都是匿名函数(如何使用命名函数的箭头函数)

首先了解什么是命名函数:

function greet(name) { // 在递归和解除绑定的时候特别有用
  console.log(`Hello ${name}`);
}

greet('lth'); // Hello lth

箭头函数要写成命名函数可:

let greet = name => {console.log(`Hello ${name}`)};
greet('lth'); // Hello lth

应用场景

1、作为构造函数,一个方法需要绑定到对象

const Person = (name, points) => {
  this.name = name; // 这里用了箭头函数,this 并没有绑定到 Jelly 这个对象上去
  this.points = points;
}

const Jelly = new Person('jelly', 5);

// new Person 会完成四个步骤:
// 生成一个新的对象、把构造函数中的 this 值指向这个新生成的对象、把这个绑定到它的原型对象、返回这个新生成的对象

Person.prototype.updatePoints = () => {
  this.points++; // 这里的 this 指向 window,所以要使用正常的 function,这样才能绑定到对象上去
  console.log(this.points)
}

// 以上代码会报错

const Person = function(name, points) {
  this.name = name;
  this.points = points;
}

const Jelly = new Person('jelly', 5);

Person.prototype.updatePoints = function() {
  this.points++;
  console.log(this.points)
}

Jelly // object  name: 'jelly', points: 5
Jelly.updatePoints(); // 6

2、当你真的需要 this 时

const button = document.querySelector('.zoom');
button.addEventListener('click', () => {
  this.classList.add('in'); // 找不到绑定的对象 button,此时的 this 指向 window
  setTimeout(() => {
    this.classList.remove('in');
  }, 2000)
})

const button = document.querySelector('.zoom');
button.addEventListener('click', function() {
  this.classList.add('in'); 
  setTimeout(() => {
    this.classList.remove('in');
  }, 2000)
})

3、需要使用 arguments 对象

// 这个功能返回所有参数的和
const sum = () => {
  return Array.from(arguments) // 在箭头函数中是没有 arguments 这个对象的
              .reduce((prevSum, value) => prevSum 
              + value, 0)
}


const sum = function() {
  return Array.from(arguments) // 这个 from 是将类数组对象转化为真正的数组对象
              .reduce((prevSum, value) => prevSum 
              + value, 0)
}

三、参数默认值

// 老方法
function userInfo(a, b) { 
  a = a || 5; // 参数默认声明,不需要重新声明
  b = b || 6;
  return a * b;
}
// ES6 新方法
function userInfo(a = 5, b = 6) { // 提高可读性
  return a * b;
}


userInfo(); // 30
userInfo(4); // 24
userInfo(undefined, 5); // 25 不是第一个值的时候,传 undefined

四、模版字符串

字符串拼接可以简化,不用各种 ++++++ 了

const person = 'Jelly';
const age = 5;
const sentence = `${person} is ${age} years old.`;
// 老方法
const template = [
  '<div class="greet">',
    '<p>Hello</p>',
  '</div>'
].join('');

console.log(template);
// 新方法,模版字符串会保留空格
const template = `
  <div class="greet">
    <p>Hello</p>
  </div>
`.trim();

console.log(template);

应用场景

1、通过模版字符串输出列表

const Jelly = {
  name: 'jelly',
  date: '2017-05-07',
  todos: [
    {
      name: 'Go to Store', completed: false
    },
    {
      name: 'Watch Movie', completed: true
    },
    {
      name: 'Running', completed: true
    }
  ]
}

const template = `
  <ul>
    ${Jelly.todos.map(todo => `
      <li>
        ${todo.name} ${todo.completed ? '✅' : '❌'}
      </li> 
    `).join('')}
  </ul>
`

document.body.innerHTML = template;

2、给传入的参数加标签

function highlight(strings, ...values) { // strings 是个数组表示中间的字符串,values 表示 ${} 那些值

  // debugger; // 这样可以看到 highlight 的组成
  const highlighted = values.map(value => `<span class="highlight">${value}</span>`);
  
  let str = '';
  strings.forEach((string, i) => str += `${string}${highlighted[i] || ''}`);
  
  return str;
  
  // 上面的 for return 也可以用 reduce 来处理
  // return strings.reduce((prev, curr, i) => `${prev}${cur}${highlighted[i] || ''}`, '');
}

const user = 'Mary';
const topic = 'Learn to use markdown';
const sentence = highlight`${user} has commented on your topic ${topic}`;

3、过滤用户输入

防止用户在输入框的内容中,插入非法字符串或脚本来实现 XSS (跨站脚本攻击),她们可以从在实现获取 Cookie、Session、密码之类的敏感信息

使用到的库:DOMPurify.js

下面这个案例就过滤掉了内嵌的 onload 事件

function sanitize(strings, ...values) {
  const dirty = strings.reduce((prev, curr, i) => `${prev}${curr}${values[i] || ''}`, '');
  return DOMPurify.sanitize(dirty);
}

addCommentForm.addEventListener('submit', function(event) {
  event.preventDefault();
  const newComment = textarea.value.trim();
  if (newComment) {
    CommentDiv.innerHTML = sanitize`
      <div class="comment-header">${user}</div>
      <div class="comment-body">${textarea.value}</div>
    `
    
    textarea.value = '';
  }
});

五、字符串新增方法

  • .startsWith()
  • .endsWidth()
  • .includes()
  • .repeat()
const id = '51030019800730366x';
const fan = 'I love Laravist';


id.startsWith('51'); // true // 是不是以 51 开头的
id.startsWith('1980', 6); // true // 第 6 位开始是不是以 1980 开头的
 
// 区分大小写
fan.startsWith('I'); // true 
fan.startsWith('i'); // false


id.endsWith('x'); // true
id.endsWith('X'); // false
fan.endsWith('love', 6); // true

// 在 includes 方法之前我们用 indexOf

fan.indexOf('Laravist') !== -1 // true

fan.includes('Laravist') // true

fan.includes('Laravist', 10) // false 第十位开始有没有 Laravist


'哈'.repeat(10)

应用场景

1、实现右对齐

function padder(string, length = 25) {
  return `${' '.repeat(Math.max(length - string.length), 0)}${string}`
}

六、对象解构

const Tom = {
  name: 'Tom Jones',
  age: 25,
  family: {
    mother: '123',
    father: '456',
    brother: '789'
  }
}


const name = ''; // 会报错!
const { name, age } = Tom; // 先声明,然后去 Tom 对象找同名的属性

console.log(name); // 'Tom Jones'
console.log(age); // 25


// 如果你想自己声明的话,用一堆括号包裹
let name = '';
({ name, age } = Tom); // 会解析成代码块,而不是对象解构的语法

console.log(name);
console.log(age);


// 要访问 family 也可以这么写
const { fater, mother, brother } = Tom.family;

对象解构重命名

// 如果 father 已经别人提前声明了

const { father: f, mother, brother } = Tom.family; // 讲 father 重命名为 f,但要注意的是,并没有声明 father,而是声明了 f,然后去 Tom.family 里找 father 属性

console.log(f); // 123
console.log(father); // not defined
console.log(sister); // undefined

对象解构默认值

// 可设置默认值,属性值要是 undefined 的时候
const { father: f, mother, brother, sister = 'have no sister' } = Tom.family;
console.log(sister); // have no sister

常用于一些库,会提供一个 options 的选项让你来自定义属性,它们会有默认值

function appendChildDiv(options = {}) {
  const { parent = 'body', width = '100px', height = '80px', backgroundColor = 'pink' } = options;
  const div = document.createElement('div');
  div.style.width = width;
  div.style.height = height;
  div.style.backgroundColor = backgroundColor;
  
  document.querySelector(parent).appendChild(div);
}

appendChildDiv({
  parent: '.container',
  width: '200px',
  height: '150px',
  backgroundColor: 'blue'
})

七、数组解构

const numbers = ['one', 'two', 'three', 'four'];

const [one, two] = numbers;

console.log(one, two); // one two


// 想获取 1、3
const [one, ,three] = numbers;
console.log(one, three); // one three


// 获取所有
const [one, ...others] = numbers; // ...other 只能存在于最后一个,不能是 const [one, ...others, five] = numbers;
console.log(one, others); // one ['two', 'three', 'four']

数组解构默认值

const details = ['JellyBool', 'laravist.com'];

const [name, website, category = 'PHP'] = details; // 默认值为 undefined 时才生效

console.log(name, website, category); // JellyBool laravist.com PHP

应用场景

1、交换变量

// 老办法
let a = 10;
let b = 20;

let temp;

temp = a;
a = b;
b = temp;


// 新办法
[a, b] = [b, a]

八、for-of 遍历

传统的有三种方法:for、forEach、for-in

for 在于繁琐
forEach 缺点在于:不能中止 break、continue、return
for in 在于会遍历所有可枚举的属性
for-of 现在暂时不支持对象

// for-in  需要注意的是,遍历对象上可枚举的属性
friuts.describe = 'My favorite fruits';

// 或者给原型加一个方法
friuts.prototype.first = function() {
  return this[0];
}

// 新增的属性和方法都会被遍历出来

for-of 的使用

// 只需将 for in 改为 for of 即可

for (let fruit of fruits) { // 要用 let 
  if (fruit === 'Orange') { // 可中止
    break;
  }
  console.log(fruit)
}

应用场景

1、可用于可迭代对象

对象有内置了遍历器接口

fruits.entries()

// 会返回
Array Iterator{} // 这个就是它的遍历接口
const fruits = ['Apple', 'Banana', 'Orange', 'Mango'];

for (let [index, fruit] of fruits.entries()) {
  console.log(`${fruit} ranks ${index + 1} in my favorite fruits`);
}

// Apple ranks 1 in my favorite fruits
// Banana ranks 2 in my favorite fruits
// Orange ranks 3 in my favorite fruits
// Mango ranks 4 in my favorite fruits

2、转换类数组 arguments

function sum() {
  let total = 0;
  for (let num of arguments) {
    total += num;
  }
  return total;
}

sum(1,2,3,4,5,6,7,8,9,10); // 55

3、绑定事件

const lis = document.querySelector('li');
for (let li of lis) {
  li.addEventListener('click', function() {
    this.classList.toggle('completed');
  })
}

九、ES6 数组的新方法 - Array.from() and Array.of()

Array.from() 是将一个类数组对象(拥有 length 属性的)或者一个可遍历的对象(有 iterator 接口的),转换为一个真正的数组

<ul>
  <li>Go to Store</li>
  <li>Watch TV</li>
  <li>Go Shopping</li>
</ul>
const todos = document.querySelectorAll('li');

const names = todos.map(todo => todo.textContent); // 这会报错,因为 __proto__ 的是 NodeList

// 方法一,转化为真正的数组
const todosArr = Array.from(todos);
const names = todosArr.map(todo => todo.textContent);

// 方法二,简化 from 可以接受第二个参数,相当于传入一个函数
const names = Array.from(todos, todo => todo.textContent); 
console.log(names)


// arguments 例子 

function sum() {
  console.log(arguments); // 它的 __proto__ 是一个 Object
  return arguments.reduce((prev, curr) => prev + curr, 0); // 报错
  return Array.from(arguments).reduce((prev, curr) => prev + curr, 0); // YES
}


// 字符串转为数组

let website = 'laravist';
Array.from(website); // ['l', 'a' .....]

Array.of() 保证创建数组的一致性

new Array(1); // [undefined x 1]

new Array(7); // [undefined x 7]

new Array(1, 2, 3); // [1, 2, 3]

Array.of(1); // [1]

Array.of(7); // [7]

Array.of(1, 2, 3); // [1, 2, 3]

十、ES6 数组的新方法

  • .find()
  • .findIndex()
  • .some()
  • .every()
const inventory = [
  { name: 'apples', quantity: 2 },
  { name: 'bannans', quantity: 0 },
  { name: 'cherries', quantity: 5 }
]
@Jerrodly
Copy link

求更新

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants