ES6基础

蓝旭秋季自学自讲——ES6基础


一、什么是ES6

ES6, 全称 ECMAScript 6.0,是 JavaScript 的当前版本标准。ECMAScript即javascript的正式名称,1996年11月,JavaScript 的创造者网景公司将 JS 提交给国际化标准组织 ECMA,希望这种语言能够成为国际标准,随后 ECMA 发布了规定浏览器脚本语言的标准,即ECMAScript。这也有利于这门语言的开放和中立。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言,成为企业级的语言。ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。通常情况下,这两个词是可以互换的。

首先先了解一个有意思的名词,什么是语法糖~


二、ES6基础

2.1变量声明

let

ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

let与var的区别:

  1. let声明的变量不能重复使用

  2. let声明的变量不能变量提升

  3. 块状作用域

  4. 使用let声明的变量不属于顶层对象

let命令存在块级作用域

 if(true){
        var m = 100;
        let n = 30;
    }
 console.log(m);//100
 console.log(n);//Uncaught ReferenceError: n is not defined

讲解:如图所示,分别使用let和var来声明变量,在代码块外调用两个变量,结果let声明的变量报错显示undefined,var声明的变量返回正确的数值,表明let声明的变量只在他所在的代码块有效

let声明的变量不能变量提升

//var的情况
console.log(foo);
var foo = 2;   //undefined
 
//let的情况
console.log(bar);
let bar = 2;   //ReferenceError: Cannot access 'bar'

讲解:上述代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。 变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。 因此,使用let声明变量,极大的消除了代码的潜在bug的隐患。

const

  1. const 声明的常量用大写(不成文的规定吧属于是)

  2. const声明的常量不能修改

  3. 块状作用域

  4. 使用const声明的常量也不属于顶层对象


2.2 扩展运算符

①对象的扩展运算符

对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。

let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }

上述方法等价于:

let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }

Object.assign()方法用于对象的合并,将源对象的所有可枚举属性,复制到目标对象中去。Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。(如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性)。

同样,如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。

let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}};  // {a: 2, b: 4}

这里有点需要注意的是扩展运算符对对象实例的拷贝属于一种浅拷贝

浅拷贝:创建一个新对象,这个对象对原始对象的属性值有着一份精确的拷贝。如果属性是基本类型数据,拷贝的就是基本类型的值,改变原始数据属性,新对象属性不会发生改变;如果属性是引用类型,那么就是拷贝的这个属性的内存地址,如果属性值发生改变,就会影响到原始对象
深拷贝:创建一个新对象,将原始对象从内存中完整的拷贝出来,从堆内存中开辟出一个新的区域存放新对象,且修改新对象,不会影响原始对象

我们知道javascript中有两种数据类型,分别是基础数据类型和引用数据类型。基础数据类型是按值访问的,常见的基础数据类型有Number、String、Boolean、Null、Undefined,这类变量的拷贝的时候会完整的复制一份;引用数据类型比如Object,(Date、function、Array、RegExp)。在拷贝的时候拷贝的是对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化,比如:

let obj1 = { a: 1, b: 2};
let obj2 = { ...obj1, b: '2-edited'};
console.log(obj1); // {a: 1, b: 2}
console.log(obj2); //  {a: 1, b: "2-edited"}

上面这个例子扩展运算符拷贝的对象是基础数据类型,因此对obj2的修改并不会影响obj1,如果改成这样:

let obj1 = { a: 1, b: 2, c: {nickName: 'd'}};
let obj2 = { ...obj1};
obj2.c.nickName = 'd-edited';
console.log(obj1); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
console.log(obj2); // {a: 1, b: 2, c: {nickName: 'd-edited'}}

这里可以看到,对obj2的修改影响到了被拷贝对象obj1,原因上面已经说了,因为obj1中的对象c是一个引用数据类型,拷贝的时候拷贝的是对象的引用。

②数组的扩展运算符

扩展运算符同样可以运用在对数组的操作中。eg:将数组转换为参数序列

function add(x, y) {
  return x + y;
}

const numbers = [4, 38];
add(...numbers) // 42

可以复制数组
如果直接通过下列的方式进行数组复制是不可取的,arr2的变化会直接影响到arr1

const arr1 = [1, 2];
const arr2 = arr1;
arr2[0] = 2;
arr1 // [2, 2]

原因上面已经介绍过,用扩展运算符就很方便:

const arr1 = [1, 2];
const arr2 = [...arr1];

还是记住那句话:扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中,这里参数对象是个数组,数组里面的所有对象都是基础数据类型,将所有基础数据类型重新拷贝到新的数组中。

扩展运算符可以与解构赋值结合起来,用于生成数组

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

需要注意的一点是:

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

const [...rest, last] = [1, 2, 3, 4, 5];// 报错
const [first, ...rest, last] = [1, 2, 3, 4, 5];// 报错
const [first, ...rest] = [1,2,3,4,5];//正确

扩展运算符还可以将字符串转为真正的数组

[...'hello']
// [ "h", "e", "l", "l", "o" ]

任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组,比较常见的应用是可以将某些数据结构转为数组:

// arguments对象
function foo() {
  const args = [...arguments];
}

用于替换es5中的Array.prototype.slice.call(arguments)写法。


2.3 箭头函数

//原先的写法
var fn = function(x,y){
	console.log(x+y);
}

//箭头函数,去除function()和{}之间用=>
var fn =(x,y)=>{
	console.log(x+y);
}
fn(2,3);

//如果函数体只有一句话,可以直接省略return 和{}
var fn=(x,y)=>console.log(x+y);	//省略{}
var fn=(x,y)=>x+y;				//省略return
fn(2,3);

箭头函数的性质

1、箭头函数没有arguments的概念,只有剩余参数

var add=()=>{
	console.log(arguments);
}
add(1,2);//报错

var add=(x,y,...a)=>{
	a.push(x,y);
	console.log(a);
}
add(1,2,3,2,4);

2、箭头函数不能当做构造函数使用,即不能使用new命令

var Product=(titile)=>{
	this.title = title;
}
var p = new Product("kh");//报错,Product不是构造函数

3、箭头函数中的this,指向的是定义时所在的对象,不是使用时所在的对象

var stu = {
	name:'zs'
}
function fn(){
	console.log(this);//指向window
}
var fn2=()=>{
	console.log(this);
}

//让fn的this指向stu:
fn.call(stu);//修改成功

//让fn2的this指向stu:
fn2.call(stu);//修改失败

2.4 函数传参

默认参数值
用法:function name(arg = defaultValue) { … }

function fn(arg = 'foo'){
    console.log(arg);
}
fn();  // foo
fn('bar');  // bar

剩余参数

在 ES6 发布之前函数的定义中存在一个名为 arguments 的对象,该对象用于在函数体内访问当前被调用时所传入的参数列表。这个 arguments 是一个类数组对象(Array-like Object),即 arguments 以自然数(0、1、2等)作为属性名并以相对应的传入参数值作为属性值,所以 arguments 对象并不具备数组类型所具有的方法等,因此在开发中经常会使用 Array.slice 来将 arguments 转换为一个真正的数组。
在 ES6 中同样为 Array 这个对象添加了一个新的方法 Array.from,这个方法作用就是将一些可以被转换为数组的对象转换为数组,其中最主要的就是类数组对象。

function fn(){
    console.log(Array.from(arguments));
}

fn(1, 2, 3, 4, 5);   // 1 2 3 4 5

剩余参数用法:function fn([arg, ] ...restArgs){}

function fn(foo, ...rest){
    console.log(`foo: ${foo}`);
    console.log(`Rest Arguments: ${rest.join(',')}`);
}

fn(1, 2, 3, 4, 5); 
//输出结果为
//foo: 1 
//Rest Arguments: 2,3,4,5

语法使用的注意事项

一旦一个函数的参数列表中使用了剩余参数的语法糖,便不可以再添加任何参数,否则会抛出错误。

function fn1(...rest) { /* ... */ }  // Correct
function fn2(...rest, foo) { /* ... */ }  // Syntax Error

arguments 与剩余参数
虽然从语言角度看,arguments 和 …args 是可以同时使用的,但有一种情况除外—— arguments 在箭头函数中,会跟随上下文绑定到上层,所以在不确定上下文绑定结果的情况下,尽可能不要在箭头函数中使用 arguments ,而要使用 …args,除非在特殊的场景下需要使用到 arguments.callee 和 arguments.caller。

解构传参
使用数组作为传入参数以控制函数的调用情况,但不会替换函数调用中的上下文。

function sum(...numbers){
    return numbers.reduce((a, b) => a+b);
}

sum(...[1,2,3]);  // 6

2.5 解构赋值

ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值。

数组的解构

const arry = ['num', 'string', 'symbol']
let [a, b, c] = arry
console.log(a)//num

对象的解构

和数组解构方法类似,将原对象的值赋予新对象然后打印新对象里面的值(对象解构赋值时一定要注意等号两边键名相等)。

const {a,b} = {a: 11,b: 22}
console.log(a)//11

三、ES6新特性

3.1 数组新特性

一、扩展运算符的应用

1、ES6通过扩展元素符…,好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
主要用于函数调用的时候,将一个数组变为参数序列,可以将某些数据结构转为数组,能够更简单实现数组复制,数组的合并也更为简洁。

2、扩展运算符可以与解构赋值结合起来,用于生成数组。如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错,可以将字符串转为真正的数组。

3、定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正的数组。如果对没有 Iterator 接口的对象,使用扩展运算符,将会报错。

二、构造函数新增的方法

1、Array.from():将两类对象转为真正的数组:类似数组的对象和可遍历的对象(包括 ES6 新增的数据结构 Set 和 Map)。可以接受第二个参数,用来对每个元素进行处理,将处理后的值放入返回的数组。

2、Array.of():用于将一组值,转换为数组

  • 没有参数的时候,返回一个空数组
  • 当参数只有一个的时候,实际上是指定数组的长度
  • 参数个数不少于 2 个时,Array()才会返回由参数组成的新数组

三、实例对象新增的方法

1、copyWithin():将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组

参数:

  • target(必需):从该位置开始替换数据。如果为负值,表示倒数。
  • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

2、find()、findIndex():
find()用于找出第一个符合条件的数组成员,参数是一个回调函数,接受三个参数依次为当前的值、当前的位置和原数组。
findIndex返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1

这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

3、fill():使用给定值,填充一个数组

还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
注意,如果填充的类型为对象,则是浅拷贝。

4、entries(),keys(),values():

keys()是对键名的遍历
values()是对键值的遍历
entries()是对键值对的遍历

5、includes():用于判断数组是否包含给定的值

方法的第二个参数表示搜索的起始位置,默认为0,参数为负数则表示倒数的位置

6、flat(),flatMap():将数组扁平化处理,返回一个新数组,对原数据没有影响

flat():默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。
flatMap()方法对原数组的每个成员执行一个函数相当于执行Array.prototype.map(),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。flatMap()方法还可以有第二个参数,用来绑定遍历函数里面的this。

四、数组的空位

数组的空位指,数组的某一个位置没有任何值,ES6 则是明确将空位转为undefined,包括Array.from、扩展运算符、copyWithin()、fill()、entries()、keys()、values()、find()和findIndex()。

3.2 字符串新特性

模板字符
使用反引号 ``

let html=`
	<div>
		<p>哈哈</p>
	</div>
`;

作用:

  1. 可以解析变量
  2. 可以换行
  3. 空格和回车也会输出出来

在变量和常量拼接的时候使用,整个字符串使用${变量}:

var tag = document.createElement('li');
tag.innerHTML = `
	<img src="" alt="">
	<p>${变量}</p>
`;
father.appendChild(tag);

标签模板,模板字符串不仅可以按照上面这种方式使用,还可以跟在一个函数后面,则表示该函数将被调用来处理某个模板字符串。

alert(123);
等价于=
alert`123`;
/
function add(x,y){
	console.log(x,y);
}
add(1,2);

//此时如果使用的是模板字符串,如果参数是常量,会把参数解析成一个数组,合并成一个参数,例如:
add`1 2`;
add`1,2`;
//如果参数有常量有变量,会把常量拼接成一个数组,每一个变量依次拼接到最后面
let a=10;
let b=2;
add`hello${a}welcome${b}`;
第一个参数:['hello','welcome','']
第二个参数:10
第三个参数:2

repeat重复一个字符串;

let str =‘hellow’;
console.log(str.repeat(2));
//hellowhellow
console.log('a'.repeat(3));
//aaa

字符串补全长度
padStart()在前面补充到指定的长度

let s = 'aaa';
s.padStart(10)  一个参数默认补充空格
s.padStart(10,'*')在前面填充第二个参数的内容 //*******aaa

padEnd() 在字符串后面补充到指定的长度

字符串包含验证
indexOf() 返回字符串指定的位置
includes(); 查找字符串是否包含 “指定的字符”:,如果包含返回true,不包含返回false
例子:

var str = "Hello world, welcome to the Runoob。";
var n = str.includes("world"); //true

startsWith()判断是否是指定字符串开头
endsWith()判断是否指定字符串结尾
String.startswith(‘ab’):接受一个参数,参数是要检索的字符串。判断当前字符串是否以另一个字符串作为开头。
例子:

// var a  = "abcd".startsWith("abc"); 
//console.log(a) //true

String.endsWidth:接受一个参数,参数是要检索的字符串。判断当前字符串是否以另一个字符串结尾。


四、ES6模块化

在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了AMD、CMD、CommonJS等模块化规范。但是,这些由社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通用的模块化标准——AMD 和 CMD 适用于浏览器端的 Javascript 模块化,CommonJS 适用于服务器端的 Javascript 模块化。

ES6 模块化规范
意义:
① ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。

定义:
① 每个 js 文件都是一个独立的模块
② 导入其它模块成员使用 import关键字
③ 向外共享模块成员使用 export关键字

用法:
① 默认导出与默认导入
② 按需导出与按需导入
③ 直接导入并执行模块中的代码

默认导出的语法:
export default 默认导出的成员

默认导入的语法:
import 接收名称 from '模块标识符'

let n1 = 10 //定义模块私有成员n1
let n2 = 20 //定义模块私有成员n2 (外界访问不到n2 因为他没有共享出去)

function show() {} //定义模块私有方法 show

export default { //使用export default 默认导出语法 向外共享n1 和 show 两个成员
    n1,show
}

注意点:
① 每个模块中,只允许使用唯一的一次export default,否则会报错
② 默认导入时的接收名称可以任意名称,只要是合法的成员名称即可

按需导入语法:
export 类型 成员

按需导出语法:
import { 成员 } from '模块标识符'

import aixos from '@/utils/request.js'

// login 请求
export const userLogin = (data) => {
    return aixos({
        method: 'post',
        url: '/login',
        data
    })
}

// register 请求
export const userRegister = (data) => {
    return aixos({
        method: 'post',
        url: '/register',
        data
    })
}

注意:
① 每个模块中可以使用多次按需导出
② 按需导入的成员名称必须和按需导出的名称保持一致
③ 按需导入时,可以使用 as 关键字进行重命名
④ 按需导入可以和默认导入一起使用


五、ES6面向对象

5.1 什么是面向对象

  1. 面向过程编程POP
    面向过程(Process-oriented programming)就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个地依次调用就可以了。

  2. 面向对象OOP
    面向对象(Object Oriented Programming)是把实务分解成一个个对象,然后由对象之间分工合作。先找出对象,并写出这些对象的功能。
    面向对象是以对象功能来划分问题,而不是步骤。

面向过程和面向对象的对比

  • 面向过程
    优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。
    缺点:没有面向对象易维护、易复用、易扩展。
  • 面向对象
    优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使系统更灵活、更易于维护。
    缺点:性能比面向过程低。

5.2 面向对象的特点

面向对象具有封装、继承、多态这三大特性。

在面向对象程序开发思想中,每个对象都是功能中心,具有明确分工。面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型项目。


5.3 ES6中的面向对象

1、类 class
ES6中新增了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象的。

类抽象了对象的公共部分,它泛指某一大类(class)。
对象特指某一个,通过类实例化一个具体的对象。

2、创建类和对象

// 1.创建类class
class Star {
    constructor(uname, age) {
        this.uname = uname;
        this.age = age;
    }
}
// 2.利用类创建对象new
new Star();

3、类constructor构造函数
constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()。

4、类添加方法
直接写到类中,注意不需要写function关键字。多个函数方法之间不需要添加逗号分隔。

class Star {
        constructor(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        // 方法不需要使用function关键字,方法之间不需要加逗号
        // 可以传入参数
        sing(song) {
            console.log(this.uname + '唱' + song);
        }
    }
    // 2.利用类创建对象new
    var ldh = new Star('刘德华');
    console.log(ldh);
    ldh.sing('《冰雨》');

5、类的继承
程序中的继承:子类可以继承父类的一些属性和方法。

  • extends关键字
    子类继承父类中的money()方法。
// 1、类的继承
class Father {
    constructor() {

    }
    money() {
        console.log(100);
    }
}
class Son extends Father {

}
var son = new Son();
son.money();

但是,下面代码会报错。因为父类中的sum()方法,this指向的是父类的数据,而子类在调用时,会发生错误。

class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
        }
        var son = new Son(1, 2);
        son.sum();

由此,出现super关键字。

  • super关键字
    使用super关键字,调用父类的方法。将子类对象传入的参数调用父类的构造函数,实现计算。
class Son extends Father {
            constructor(x, y) {
                // this.x = x;
                // this.y = y;
                super(x, y);  // 调用父类中的构造函数
            }
        }

super关键字用于访问和调用对象父类上的函数。除了可以调用父类的构造函数,还可以调用父类的普通函数。

不使用super关键字

class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
            say() {
                return '我是爸爸';
            }
        }
        class Son extends Father {
            // 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
            // 如果子类没有,就去查找父类有没有这个方法,如果有。则执行父类的方法(就近原则)
            say() {
                console.log('我是儿子');
            }
        }
        var son = new Son();
        son.say();

此时,直接调用子类中的say方法。

继承中的属性或者方法的查找原则:
1、继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
2、如果子类没有,就去查找父类有没有这个方法,如果有。则执行父类的方法(就近原则)

使用super关键字

class Son extends Father {
            say() {
                // console.log('我是儿子');
                // super.say()就是调用父类中的普通函数say()
                console.log(super.say() + '的儿子');
            }
        }

使用super.say()调用父类中的普通函数say()。

6、子类继承父类的方法同时扩展自己方法
子类继承父类的sum()方法,同时扩展自己的substract()方法。

// 父类有加法方法
class Father {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    sum() {
        console.log(this.x + this.y);
    }
}
// 子类继承父类的加法,同时扩展减法
class Son extends Father {
    constructor(x, y) {
        // 利用super() 调用父类的构造函数
        // super 必须在子类this之前调用
        super(x, y);
        this.x = x;
        this.y = y;
    }
    substract() {
        console.log(this.x - this.y);
    }
}
var son = new Son(5, 3);
son.substract();
son.sum();

注意,子类中利用super()调用父类的构造函数时,super一定要在子类this之前调用。

使用类的注意事项

  1. 类里面共有的属性和方法一定要加this使用
  2. 在ES6中,类没有变量提升,所以必须先定义类,才能通过类实例化对象
class Star {
            constructor(uname, age) {
                // 2、类里面共有的属性和方法一定要加this使用
                this.uname = uname;
                this.age = age;
                // this.sing();
                this.btn = document.querySelector('button');
                this.btn.onclick = this.sing;  // sing不要加括号,不然会立即执行
            }
            sing() {
                // console.log('hi');
                console.log(this.uname);
            }
        }
        // 1、在ES6中,类没有变量提升,所以必须先定义类,才能通过类实例化对象
        var ldh = new Star('刘德华');

7、this的指向问题

var that;
var _that;
class Star {
    constructor(uname, age) {
        // 3、constructor里面的this指向的是创建的实例对象
        that = this;
        console.log(this);
        this.uname = uname;
        this.age = age;
        this.btn = document.querySelector('button');
        this.btn.onclick = this.sing;
    }
    sing() {
        // sing方法中的this指向的是btn,因为这个按钮调用了这个函数
        console.log(this.uname);  // undefined
        console.log(that.uname);  // that中存储的是constructor中的this
    }
    dance() {
        // dance里面的this 指向的也是实例对象,因为该实例对象调用了这个函数
        _that = this;
        console.log(this);
    }
}
var ldh = new Star('刘德华');
console.log(that === ldh);
ldh.dance();
console.log(_that === ldh);

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

  1. sing()方法中的this指向的是btn,因为是这个按钮调用了这个函数:所以打印this.uname输出的是undefined;如果想输出constructor中的uname,则需要在外面声明一个全局变量that,并将constructor中的this用that存储起来,用于之后的调用。

  2. dance()方法中的this指向的是实例对象,因为是实例对象ldh调用了这个函数


End

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值