当前位置:   article > 正文

JavaScript 进阶面向对象ES6_javascript '@localizer[""]

javascript '@localizer[""]

ES6类的本质

ES6之前 → 通过 构造函数 + 原型 实现面向对象编程

  1. 构造函数有原型对象 prototype
  2. prototype里有 constructor 指向构造函数本身
  3. 可以通过原型对象添加方法
  4. 创建实例对象有__proto__原型指向构造函数的原型对象

类的本质

  1. class本质还是一个 function
  2. 类的所有方法都定义在类的prototype属性上
  3. 类创建的实例,里面也有__ proto __指向类的prototype原型对象
  4. class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语言,所以ES6的类其实就是语法糖

语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖。


ES6面向对象

1. 类的创建

创建类
class name{
    // class body
}
// 创建实例  类必须使用new 实例化对象
var xx = new name();
  • 1
  • 2
  • 3
  • 4
  • 5

2. constructor构造函数

  • 类的构造函数(默认方法),用于传递参数,返回实例对象.
  • 通过new生成对象实例时自动调用该方法.
  • 如果没有显示定义,类内部会自动创建.
class Person{
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    say(){
        console.log(this.name + 'Hello!');
    }
}
// 创建实例
var ldh = new Person('刘德华',18);
console.log(ldh.name)     // 刘德华
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 方法之间不能添加逗号,方法也不需要function关键字.

3. 类的继承

子类可以继承父类的一些属性和方法。

class Father{   // 父类
}
class Son extends Father{  // 子类继承父类    
}
  • 1
  • 2
  • 3
  • 4

实例:

class Father{
    constructor(surname){
        this.name = surname;
    }
    say(){
        console.log('你的姓是' + this.name);
    }   
}
class Son extends Father{   // 子类继承了父类
    
}
var ss = new Son('廖')
ss.say();  // 你的姓是廖
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 继承中的属性或者方法查找原则:就近原则

4. super 关键字

调用父类的构造函数,也可以调用父类的普通函数。

class Father{
    constructor(surname){
        this.surname = surname;
    }  
    saySurname(){
        console.log('我的姓是' + this.surname);
    }
}
class Son extends Father{
    constructor(surname,firstname){  // 子承父类

        super(surname);  // 调用父类constructor(surname)
        
        this.firstname = firstname;   // 定义子类独有属性
    }
    sayFirstname(){
        console.log('我的名字是:' + this.firstname);
    }
    
}
var ss = new Son('吴','彦祖')
ss.saySurname();
ss.sayFirstname();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

调用父类的普通函数

class Father{
    say(){
        return '我是父类 -->'
    }
}
class Son extends Father{
    say(){
        // super.say() super 调用父类方法
        return super.say() + '的子类';
    }
}
var dome = new Son();
console.log(dome.say());   // 我是父类!的子类
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

super 必须在子类this之前调用。

子类在构造函数中使用super, 必须放到 this 前面 (必须先调用父类的构造方法,在使用子类构造方法)

注意点

  • ES6中没有变量提升(变量的提前声明),所以必须先定义类,才能实例化对象。
  • 类里面的共有属性和方法一定要加 this 使用。

类里面的 this 指向问题:

  • constructor里面的 this 指向实例对象,方法里的 this 指向这个方法的调用者。

一些方法:

用法说明
insertAdjacentHTML(‘追加的位置’,‘追加的字符’)用字符串创建的元素追加到父元素里面
beforeend插入元素内部的最后一个子节点之后
ondblclick双击事件
input.select()文本框内容处于选中状态

双击禁止选中文字:

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
  • 1

insertAdjacentHTML(position,text);

// 创建li元素和section元素
var li = '<li class="liactive"><span>测试1</span><span class="iconfont icon-guanbi"></span></li>';

// 把这两个元素追加到对应的父元素里面  beforeend(父元素内容的后面)
that.ul.insertAdjacentHTML('beforeend', li)
  • 1
  • 2
  • 3
  • 4
  • 5

ES6新增语法

let 声明变量关键字

  1. let 声明的变量时只在所处于的块级有效

    if (true) {
        let b = 20;
        console.log(b)
        if (true) {
            let c = 30;
        }
        console.log(c);  // c is not defined
    }
    console.log(b) // b is not defined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  2. 不存在变量提升

    console.log(a);  // a is not defined
    let a = 20;   
    
    • 1
    • 2
  3. 存在暂时性死区问题

    var num = 10
    if (true) {
        console.log(num);   // Cannot access 'num' before initialization
        let num = 20;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  4. 防止循环变量变成全局变量

    ```js
    for (let i = 0; i < 5; i++) {
      console.log(i);
    }
    // 依次输出 0 1 2 3 4 5
    ```
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

const 声明常量

作用:声明常量,常量就是值(内存地址) 不能变化的量

  1. 使用const关键字声明的常量具有块级作用域
     if (true) {
         const a = 10;
         if (true) {
             const a = 20;
             console.log(a);  // 20
         }
         console.log(a); // 10
     }
     console.log(a);   //  a is not defined
     ```



2.  声明常量时必须赋值

```js
const PI; // Missing initializer in const declaration
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. 常量赋值后,值不能修改, 但是可以替换数组内的值。
const = PI;
PI = 100; // assignment to constant variable.   
 

const ary = [100, 200];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b'];  
ary = ['a','b']; // assignment to constant variable. 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

解构赋值

ES6 中允许从数值中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

数值解构

// 数组解构允许我们按照一一对应的关系从数组中提取值 然后将值赋值给变量
let ary = [1,2,3];
let [a, b, c, d, e] = ary;
console.log(a,b,c)  // 1 2 3
console.log(e)  // undefined

// 如果解构不成功,变量的值为undefined
let [foo] = [];
let [bar, foo] = [1]; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对象解构

let person = { name: 'zhangsan', age: 20 }; 

// 从 person对象中解构(匹配) name 和 age 属性
let { name, age } = person;
console.log(name); // 'zhangsan'
console.log(age); // 20    
          
let {name: myName, age: myAge} = person; // myName myAge 属性别名
console.log(myName); // 'zhangsan' 
console.log(myAge); // 20 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

箭头函数

写法:

() => {}    
const fn = () => {}
  • 1
  • 2
  1. 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
function sum(num1, num2) {
    return num1 + num2;
}    
const sum = (num1, num2) => num1 + num2;
  • 1
  • 2
  • 3
  • 4
  1. 如果形参只有一个,可以省略小括号
function fn(v){
    retuen v;
} 
const fn = v => v;
  • 1
  • 2
  • 3
  • 4
  1. 箭头函数不绑定this,没有自己的this关键字,在箭头函数中 this关键字将指向箭头函数定义位置中的this。
const obj = { name: '张三'}
function fn () {
    console.log(this); // obj 
    return () => {
        console.log(this) // obj 
    }
}
const resFn = fn.call(obj);
resFn();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

箭头函数面试题:

var age = 100;
var obj = {
    age: 20,
    say: () => {
        alert(this.age)  // 100
    }
}
obj.say();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

剩余参数

在最后的形参名前加 ...

  1. 剩余参数语法允许我们将一个不定数量的参数表示( 存放至)为一个数组.
function sum (first, ...args) {
    console.log(first); // 10 
    console.log(args); // [20, 30]
}
sun(10, 20, 30);
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 剩余参数和解构配合使用
let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students;
console.log(s1); // wangwu
console.log(s2); // ['zhangsan', 'lisi']
  • 1
  • 2
  • 3
  • 4

ES6 新增方法

数组方法

扩展运算符 (展开语法)
  1. 将数组或者对象转为用逗号分隔的参数序列
let aty = [1, 2, 3];

// ...ary  // 1, 2, 3
console.log(...ary);  // 1 2 3 
console.log(1, 2, 3);  // 1 2 3
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 使用扩展运算符可以应用于合并数组
//方法一 
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2]; 

// 方法二
ary1.push(...ary2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 将类(伪)数组或可遍历对象转换为正真的数组
let oDivs = document.getElemenetsByTagName('div');   // HTMLCollection(10)
oDivs = [...oDivs];   // Array(7)
  • 1
  • 2

构造函数方法

  1. 将类(伪)数组或可遍历对象转换为正真的数组
 let arrayLike = {
     '0': 'a',
     '1': 'b',
     '2': 'c',
     length: 3
 };
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 可以传入第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
let arrayLike = {
    "0": 1,
    "1": 2,
    "length": 2
}
let newAry = Array.from(aryLike, item => item * 2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

实例方法

find()

返回第一个符合条件元素,如果没有找到返回undefined

let ary = [{
    id: 1,
    name: '张三' 
},{
    id: 2,
    name: '李四'
}];
let target = ary.find((item, index) => item.id == 2);  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
findIndex()

返回第一个符合条件元素索引位置,如果没有找到返回undefined

let ary =[1, 5, 10, 15];
let index = ary.findIndex((value,index) => value > 9);
console.log(index); // 2   
  • 1
  • 2
  • 3
includes()

查找某个数组是否包含给定的值,返回布尔值。

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false

let ary = ["a", "b", "c"];
console.log(ary.includes('a'))
  • 1
  • 2
  • 3
  • 4
  • 5

模板字符串

ES6 新增的创建字符串的方式,使用反引号定义。

1、解析变量
let name = '张三';
let sayHello = `hello,my name is ${name}`; // hello,my name is zhangsan 
  • 1
  • 2
2、模板字符串换行
let resilt = {
    name: 'zhangsan',
    age: 20,
    sex:'男'
}
let html = ` <div>
    <span>${result.name}</span>
    <span>${result.name}</span>
    <span>${result.name}</span>
</div> `;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
3、在模板字符串可以调用函数
colst aryHello = function () {
    return '你好 世界!';
};

let greet = `${sayHello()} 哈哈哈哈`;
console.log(greet);  // 你好 世界!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

字符串方法

startsWith()
  • 表示参数字符串是否在原字符串的头部,返回布尔值
endsWith()
  • 表示参数字符串是否在原字符串的尾巴,返回布尔值
let str = 'Hello world!';
str.startsWith('Hello') // true
str.endsWith('!')       // true
  • 1
  • 2
  • 3
repeat()
  • 将原字符串重复n次,返回一个新字符串。
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
  • 1
  • 2

Set 数据结构

  • ES6 提供了新的数据结构 Set 它类似于数组 但是成员的值都是唯一的 没有重复的值。
  • Set本身是一个构造函数,用来生成 Set 数据结构。
  • Set函数可以接受一个数组作为参数,用来初始化。
// 创建一个set() 数据结构
const s = new Set;

const set = new Set([1, 2 ,3 ,4 , 5]);
  • 1
  • 2
  • 3
  • 4

set() 实例方法

方法说明
add(value)添加某个值,返回Set结构本身
delete(value)表示删除是否成功, 返回布尔值
has(value)表示该值是否为Set的成员, 返回布尔值
clear()清除所有成员

示例:

const s = new Set();
s.add(1).add(2).add(3);     // 向 set 结构中添加值
s.delete(2)                 // 删除 set 结构中的2值
s.has(1)                    // 表示 set 结构中是否有1这个值 返回布尔值 
s.clear()                   // 清除 set 结构中的所有值                     
  • 1
  • 2
  • 3
  • 4
  • 5
set() 遍历
  • Set 结构的实例与数组一样,也拥有forEach方法。
// 遍历set数据结构 从中取值
const s = new Set(['a', 'b', 'c']);
s.forEach(value => {
    console.log(value)
})
  • 1
  • 2
  • 3
  • 4
  • 5

正则表达式

正则表通常被用来提取和替换那些符合某个模式(规则)的文本。

列如验证表单、过滤敏感词、从字符串中获取我们想要的特定部分。

创建正则表达式

// 1. 利用 RegExp对象来创建 正则表达式
var regexp = new RegExp(/123/);
console.log(regexp);

// 2. 利用字面量创建 正则表达式
var rg = /123/;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
test() 检测正则表达式

正则对象方法,用于检测字符串是否符合该规则,返回 true 或 false

var rg = /123/;
console.log(rg.test(123));
  • 1
  • 2

边界符

边界符说明
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)
  • ^ 和 $ 在一起使用时,表示必须是精确匹配

字符类

  1. 表示有一系列字符可供选择,只要匹配其中一个就可以了
  2. 所有可供选择的字符都放在方括号[] 内。
var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true
console.log(rg.test('andy'));


// 2. 字符组合 匹配26个英文字母和数字 1-9
/^[a-z0-9]$/.test('a')      // true

// 3.  [^] 方括号内部 取反符^
/^[^abc]$/.test('a')        // false

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

量词符

量词符用来设定某个模式出现的次数。

量词说明
*匹配零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次

括号使用

括号说明
{} 量词符括号内表示重复次数
[]字符集合匹配方括号中的任意字符
()分组表示优先级

预定义类

预定义类指的是某些常见模式的简写方式

预定义说明等价于
\d匹配0-9之间的任一数字[0-9]
\D匹配所有0-9以外的字符[ ^0-9]
\w匹配任意的字母、数字和下划线[A-Za-z0-9_]
\W除所有字母、数字和下划线以外的字符[ ^A-Za-z0-9_]
\s匹配空格 (包括换行符、制表符、空格符等)[\t\r\n\v\f]
\S匹配非空格的字符[ ^\t\r\n\v\f]

全局匹配和忽略大小写

匹配模式说明
g全局匹配 (默认只匹配一个)
i忽略大小写
gi全局匹配+忽略大小写

正则表达式中的替换

使用replace() 方法替换,返回值是替换完毕的新字符串。

stringObject.replace(regexp/substr,replacement)

  • 第一个参数: 被替换的字符串 或者 正则表达式
  • 第二个参数: 替换的字符串
<textarea name="" id="message"></textarea> <button>提交</button>
    <div></div>

    // 替换 replace
    var text = document.querySelector('textarea');
	var btn = document.querySelector('button');
	var div = document.querySelector('div');
	btn.onclick = function() {
    div.innerHTML = text.value.replace(/激情|gay/g, '**');
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

ES5 的构造函数和原型

在 ES6之前 ,对象不是基于类创建的,而是用构建函数的特殊函数来定义对象和它们的特征。

构造函数是一种特殊的函数,使用 new 来初始化对象,把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

Snipaste_2021-09-15_21-42-59

Snipaste_2021-09-15_21-55-57

构造函数存在浪费内存问题

Snipaste_2021-09-15_22-36-49

解决办法:我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。

构造函数原型 prototype

构造函数通过原型分配的函数是所有对象所共享的。

原型的作用就是 共享方法。

对象原型 __ proto __

对象都会有一个属性__ proto __指向构造函数的 prototype原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __ proto __ 原型的存在。

__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线

实例:

function Star(uname, age) {
            this.uname = uname;
            this.age = age;
 }
// 我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上
Star.prototype.sing = function() {
    console.log('我会唱歌');
}
var ldh = new Star('薛之谦', 18);
var zxy = new Star('灵境', 19);
console.log(ldh.sing === zxy.sing);  // true
ldh.sing();
zxy.sing();

//  对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象 prototype
console.log(ldh); 

console.log(ldh.__proto__ === Star.prototype);   // true

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • __ proto __ 对象原型和原型对象 prototype 是等价的

  • 方法的查找规则: 首先先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的 sing

  • 如果么有sing 这个方法,因为有__proto__的存在,就去构造函数原型对象 prototype 身上去查找sing这个方法。

constructor 构造函数

对象都会有一个属性 __ proto __ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __ proto __ 原型的存在。

constructor 主要用于记录该对象引用那个构造函数,它可以让原型对象重新指向原来的构造函数。

注意:很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数。

function Star(uname, age) {
            this.uname = uname;
            this.age = age;
}
// 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
// Star.prototype.sing = function() {
//     console.log('我会唱歌');
// };
// Star.prototype.movie = function() {
//     console.log('我会演电影');
// }
Star.prototype = {
    // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
    constructor: Star,
    sing: function() {
        console.log('我会唱歌');
    },
    movie: function() {
        console.log('我会演电影');
    }
}
var ldh = new Star('薛之谦', 28);
var zxy = new Star('灵境', 26);
console.log(Star.prototype);  // Object
console.log(ldh.__proto__);   // Object
console.log(Star.prototype.constructor); 
console.log(Star.prototype.constructor  === ldh.__proto__.constructor); // true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

构造函数、实例、原型对象之间的关系

Snipaste_2021-09-16_14-58-14

原型链

Snipaste_2021-09-16_14-58-36

成员查找机制
  • 当访问一个对象的属性时首先查找这个对象自身有没有该属性。

  • 如果没有就找原型(__proto__指向的 prototype)。

  • 如果还没有就查找原型对象的原型(Object的原型对象)。

  • 一直找到 Object 为止(null)。

原型对象里面方法里面的this 指向的是 这个方法的调用者, 也就是这个实例对象.

扩展内置对象方法

通过原型对象,对原来的内置对象进行扩展自定义的方法,比如给对象增加自定义求偶数和的功能。

Array.prototype.sum = function(){
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
               sum += this[i];
            }
            return sum;
        }
var arr = [1, 2, 3];
console.log(arr.sum());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

使用对象的形式创建方法会被覆盖(如: Array.prototype = {} ),所以数组和字符串内置对象只能是 Array.prototype.xxx = function(){} 的方式。

call() 方法

call (设置调用时指向的对象, 传递其他参数…)

  • 调用函数,并修改函数运行时this指向。
function fn(x, y) {
            console.log('我想喝手磨咖啡');
            console.log(this);
            console.log(x + y);
}
var o = {
    name: 'andy'
};

// fn.call();  1. 使用call() 可以调用函数
// 2. 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
fn.call(o, 1, 2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

ES5里的继承

ES6之前并没有给我们提供extends继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。

借用父构造函数继承属性和方法

// 1. 父构造函数
function Father(uname, age) {
    // this 指向父构造函数的对象实例
    this.uname = uname;
    this.age = age;
}
// 定义共享方法
Father.prototype.money = function() {
    console.log(100000);
};

// 2 .子构造函数 
function Son(uname, age, score) {   
    // 借用父构造函数继承属性 使用call()设置调用时this为 子构造函数的this
    Father.call(this, uname, age);
    
    // this 指向子构造函数的对象实例
    this.score = score;  
}
// Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father();

// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;

// 这个是子构造函数专门的方法
Son.prototype.exam = function() {
    console.log('孩子要考试');

}
var son = new Son('张全蛋', 28, 100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

ES5方法回顾

数组方法

  • 迭代(遍历)方法:forEach()、map()、filter()、 some()、every()
forEach() 遍历数组
  • forEach适合于你并不打算改变数据的时候(打印或存入数据库)
array.forEach(function(currentValue, index, arr))
  • 1
  • currentValue : 每一个数组元素

  • index :数组当前项的索引

  • arr : 数组对象本身

map() 数组遍历
  • 适用于你要改变数据值的时候。速度更快且返回一个新的数组。
  • 还可以使用 复合(composition) (map(), filter(), reduce() 等组合使用。
let arr = [1, 2, 3, 4, 5];
// 使用map将每一个元素乘以它们自身,然后筛选出那些大于10的元素,最终结果赋值给arr2。
let arr2 = arr.map(value => value * value).filter(value => value > 10);
// arr2 = [16, 25]
  • 1
  • 2
  • 3
  • 4
filter() 筛选数组

筛选时直接返回一个新数组。

array.filter(function(currentValue, index, arr))
  • 1

实例:

var arr = [12,66,88,99,56,4,3]
var newarr = arr.filter(function(value,index){
    // 筛选数组中大于20的值
    return value > = 20; 
    //return value % 2 === 0; // 返回偶数
});
console.log(newarr);   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
some () 是否满足条件

返回布尔值, 找到第一个满足条件的元素则终止循环,不再继续查找。

var arr1 = ['red', 'pink', 'blue'];
var flag1 = arr1.some(function(value) {
    return value == 'pink';
});
console.log(flag1);  // true
  • 1
  • 2
  • 3
  • 4
  • 5

filter返回一个新数组,含多个元素,find返回第一个符合条件元素。

indexof() 方法可返回某个指定字符串值在字符串中首次出现的位置。

字符串方法

trim() 删除两端空白字符
  • 不影响原字符串,返回一个新字符串。
var str = '   an  dy   ';
var str1 = str.trim();
  • 1
  • 2

对象方法

Object.keys()

获取对象自身所有的属性

var obj = {
    id: 1,
    pname: '小米',
    price: 1999,
    num: 2000
};
// 获取对象自身所有的属性
var arr = Object.keys(obj);
// 使用 Object.keys() 获取所有属性后遍历对象

arr.forEach(function(value) {
    console.log(value);
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
Object.defineProperty()

定义新属性或修改原有的属性

Object.defineproperty(obj, prop, descriptor)

  • obj: 必需,目标对象
  • prop:必需,需定义或修改的属性的名字
  • descriptor: 必需,目标属性所拥有的特性

第三个参数 descriptor 说明:

  1. 必须以对象形式{} 书写
  2. value: 设置属性值 默认为undefined
  3. writable: 设置属性值是否可以被修改。true | false 默认为false不可修改。
  4. enumerable: 目标属性是否可以被遍历。
  5. configurable: 目标属性是否可以被删除或是否可以再次修改特特性。默认为false不可删除和修改。
var obj = {
    id: 1,
    pname: '小米',
    price: 1999
};

Object.defineProperty(obj, 'address', {
    value: '小米科技有限公司',
    // 如果值为false 不允许修改这个属性值 默认值也是false
    writable: true,
    // enumerable 如果值为false 则不允许遍历, 默认的值是 false
    enumerable: true,
    // configurable 如果为false 则不允许删除这个属性 默认为false
    configurable: true
});
console.log(obj.address);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

函数进阶

定义方法

Snipaste_2021-09-27_12-30-14

调用方式

  1. 普通函数
function fn() {
    console.log('Hello !');

}
// fn();   fn.call()
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 对象的方法
var o = {
    sayHi: function() {
        console.log('人生的巅峰');

    }
}
o.sayHi();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 构造函数
function Star() {
    // do somesing...
};
new Star();
  • 1
  • 2
  • 3
  • 4
  1. 绑定事件函数
 btn.onclick = function() {};   // 点击了按钮就可以调用这个函数
  • 1
  1. 定时器函数
setInterval(function() {}, 1000);  // 这个函数是定时器自动1秒钟调用一次
  • 1
  1. 立即执行函数
// 立即执行函数是自调用
(function() {
    console.log('hello 2021');
})();
  • 1
  • 2
  • 3
  • 4

this 指向问题

调用方式的不同决定了this 的指向不同。

调用方式this指向
普通函数调用window
构造函数调用(创建的)实例对象 原型对象里面的方法指向实例对象
对象方法调用改方法所属对象
事件绑定方法绑定事件的那个对象
定时器函数window
立即执行函数window

详解:

<button>点击</button>
    <script>
        // 函数的不同调用方式决定了this 的指向不同
        // 1. 普通函数 this 指向window
        function fn() {
            console.log('普通函数的this' + this);
        }
        window.fn();
        // 2. 对象的方法 this指向的是对象 o
        var o = {
            sayHi: function() {
                console.log('对象方法的this:' + this);
            }
        }
        o.sayHi();
        // 3. 构造函数 this 指向 ldh 这个实例对象 原型对象里面的this 指向的也是 ldh这个实例对象
        function Star() {};
        Star.prototype.sing = function() {

        }
        var ldh = new Star();
        // 4. 绑定事件函数 this 指向的是函数的调用者 btn这个按钮对象
        var btn = document.querySelector('button');
        btn.onclick = function() {
            console.log('绑定时间函数的this:' + this);
        };
        // 5. 定时器函数 this 指向的也是window
        window.setTimeout(function() {
            console.log('定时器的this:' + this);

        }, 1000);
        // 6. 立即执行函数 this还是指向window
        (function() {
            console.log('立即执行函数的this' + this);
        })();
    </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

改变函数内部 this 指向:

使用 bind()、call()、apply() 三种方法可以优雅的解决函数内部的 this 指向问题。

call() 和 apply() 调用函数同时改变this指向
var o = {
    name: 'andy'
}
function fn(a, b) {
    console.log(this);
    console.log(a + b);
};
// 1. call() 调用函数同时改变this指向
fn.call(o, 1, 2);
//  apply() 调用函数同时改变this指向,参数必须是数组(伪数组)
fn.apply(o, ['pink']);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
bind() 不调用函数改变this指向

示例: 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮:

<button>点击</button>
<button>点击</button>
<button>点击</button>

var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                this.disabled = true;
                setTimeout(function() {
                    this.disabled = false;
                }.bind(this), 2000);
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

总结:

相同点:

  • 都可以改变函数内部 this 指向。

不同点:

  • call 和 apply 会调用函数,并且改变函数内部this指向;
  • bind 不会调用函数,可以改变函数内部this指向

应用场景:

  • call() 经常做继承;
  • apply() 经常跟数组有关系,如借助数学对象求 max | min;
  • bind() 在不想调用函数又想改变this指向时使用,如改变定时器内部的this指向。

高阶函数

回调函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。

function fn(a, b, callback) {
    console.log(a + b);
    callback && callback();
}
fn(7, 10, function() {
    console.log('我是最后调用的');
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用,最典型的就是作为回调函数

递归函数

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

  1. 递归函数的作用和循环效果一样
  2. 由于递归很容易发生 “栈溢出”错误,所以必须要加 退出条件 return

实例: 利用递归函数求1~n的阶乘

// 利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
function fn(n) {
    if (n == 1) {
        return 1;
    }
    return n * fn(n - 1);
}
console.log(fn(3));
console.log(fn(4));

// 详细思路 假如用户输入的是 3
//return  3 * fn(2)
//return  3 * (2 * fn(1))
//return  3 * (2 * 1)
//return  3 * (2)
//return  6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

实例2: 利用递归函数求斐波那契数列 (兔子序列)

//   1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值

function fb(n) {
    if (n === 1 || n === 2) {
        return 1;
    }
    return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));
console.log(fb(6));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

闭包

变量作用域

分为全局变量局部变量

  1. 函数内部可以使用全局变量
  2. 函数外部不可以使用局部变量
  3. 当函数执行完毕,本作用域的局部变量将会被销毁。

什么是闭包 ?

一个作用域可以访问另外一个函数内部的局部变量。

闭包的作用:

延伸了变量的作用范围

我们函数外面的作用域可以访问函数内部的局部变量。

 function fn1(){    // fn1 就是闭包函数
    var num = 10;
    function fn2(){
      console.log(num); // 10
    }
       fn2()
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

思考题1:

var name = "The Window";
var obj1 = {
    name: "My Object",
    getNameFunc: function () {
        return function () {
            return this.name;
        };
    }
};

console.log(obj1.getNameFunc()())    // The Window
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

思考题2 :

var name = "The Window";
var obj2 = {
    name: "My Object",
    getNameFunc: function () {
        var that = this;
        return function () {
            return that.name;
        };
    }
};
console.log(obj2.getNameFunc()())    // My Object
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

浅拷贝和深拷贝

  1. 浅拷贝只是拷贝一层,更深层次对象级别的值拷贝引用
  2. 深拷贝拷贝多层,每一级别的数据都会拷贝。
ES6新增方法可以浅拷贝

Object.assign(target, …sources)

var obj = {
    id: 1,
    name: 'andy',
    msg: {
        age: 18
    }
};
var o = {};
Object.assign(o, obj);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

严格模式

  1. 为整个 js 脚本开启严格模式
<script>
    "use strict";
	console.log("这是严格模式。");
</script> 
  • 1
  • 2
  • 3
  • 4

有的 script 基本是严格模式,有的 script 脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在立即执行的匿名函数之中,这样独立创建一个作用域而不影响其他 script脚本文件。

(function(){
    "use strict";
    var num = 10;
    function fn(){}
})();
  • 1
  • 2
  • 3
  • 4
  • 5

为函数开启严格模式

// 此时只是给fn函数开启严格模式
function fn() {
    'use strict';
    // 下面的代码按照严格模式执行
}
  • 1
  • 2
  • 3
  • 4
  • 5
严格模式变化

变量常规:

  1. 没有变量声明提前,变量名必须先声明再使用;
  2. 严禁删除已声明的变量;

严格模式下 this 指向问题:

  1. 严格模式下全局作用域中函数中的this 是 undefined,非Windows;
  2. 构造函数不加new调用,this 会报错;
  3. new 实例化的构造函数指向创建的对象实例;
  4. 定时器 this 还是指向 window;
  5. 事件、对象还是指向调用者;
  6. 严格模式下函数里面的参数不允许有重名。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/251485
推荐阅读
相关标签
  

闽ICP备14008679号