/*P58-对象-创建对象
//JS的设计规范基于对象原型的,与其他面向对象语言类似,但更灵活。
//描述客观世界的物体,特征和行为。
//创建对象有很多方式:
//方法1:最直观的是字面值(对象初始化器),
//一个大括号,和作用域是不一样的。
//初始化器{}里面使用键值对,就是key:value的样式,可以使用键key获取对象的属性或方法对应的值
//多个属性使用逗号隔开
//键值对的值部分可以是多种类型,与变量一样。可以认为是定义在对象里面的变量
var employee={
name:'张三',
age:20,
position:'前端工程师',
signIn:function(){
console.log('张三打卡');
}
}
//方法2:使用 new object()语法,创建空对象,再去添加属性
var employee2 =new Object();
employee2.name = '李四';
employee2.signIn = function(){
console.log('李四打卡');
}
/** */
/*P59-对象-对象属性
//对象的每个键值对,都是对象的属性,可以有任何值(跟变量一样):数字,字符串,数组,函数,或者是另一个对象
//如果某个属性的值是函数,这个属性也可以叫做方法
//访问对象属性的方法,可以是.也可以用['key']数组那样方式
var employee={
name:'张三',
age:20,
position:'前端工程师',
signIn:function(){
console.log('张三打卡');
}
}
console.log(employee.name);//张三
console.log(employee['name']);//张三//注意这里没有点号,而且键名是字符串形式带引号才行。
//更新对象的属性,也用.取出,用=来赋新值,和变量一样。
employee.name='张五';
console.log(employee.name);//张五
//也可以使用数组样式的['key']的样式
employee['name']='张六';
console.log(employee.name);//张六
//可以添加没有的属性,但是不能访问没有的属性
var employee2 =new Object();
employee2.name = '李四';
employee2.signIn = function(){
console.log('李四打卡');
}
console.log(employee2.age);//undefined
employee2.age=22;
console.log(employee2.age);//22
//key一般不用引号,但是特殊情况需要
//而且加了引号,只能使用数组[]样式来访问这个属性
var employee3={
name:'王五',
//birth-date:'1990-3-2',//Uncaught SyntaxError: Unexpected token '-'
'birth-date':'1990-3-2',
}
console.log(employee3["birth-date"]);//1990-3-2//而且这里是软件自动更正,它字符串使用双引号,我一般用单引号。我刚才只输入了.birth这样,软件就能有提示了。
//实际编程的时候,建议都使用驼峰方式来命名变量和属性
//调用的时候,就可以使用.来获取属性值,比较方便
var employee4={
name:'王五',
//birth-date:'1990-3-2',//Uncaught SyntaxError: Unexpected token '-'
birthDate:'1990-3-2',
}
console.log(employee4.birthDate);//1990-3-2
/** */
/*P60-对象-省略key
//如果使用字面值方式定义对象的时候,如果属性值是一个变量,而且变量名和它的key名一样,可以省略key
//下例属性name和值name变量重名,可以只写一个
//函数的话,可以把function关键字改成属性名即可
var name='李四';
var name2='王五';
var employee5={
name : name,//这里老师没有加逗号,而且vscode给把第二个name画了横线,提示信息是"name"已弃用 ts(6385)
name2,//写成一个name也是可以的
signIn:function(){console.log('打卡');},//标号94
signIn2(){console.log('打卡')}//标号95
}
console.log(employee5.name);//李四
console.log(employee5.name2);//王五
console.log(employee5.signIn);//ƒ (){console.log('打卡');}
console.log(employee5.signIn2);//ƒ signIn2(){console.log('打卡')}
console.log(employee5.signIn());//标号94:打卡 本行标号undefined//因为没有返回值所以是undefined
console.log(employee5.signIn2());//标号95:打卡 本行标号undefined//因为没有返回值所以是undefined
/** */
/*P61-对象-遍历
//2种方法:1st使用Object.keys()这个方法;2使用forin循环
//1st使用Object.keys()这个方法;
var employee={
name:'张三',
age:20,
position:'前端工程师',
signIn:function(){
console.log('张三打卡');
}
}
console.log(Object.keys(employee));//(4) ["name", "age", "position", "signIn"]
//2nd种是forin循环,in前面是给返回的属性(key)起一个变量名字,in后面是需要遍历属性的对象
//?在P67的时候,使用for(key in emp1)会报错,Uncaught ReferenceError: key is not defined
//可以改成for(key in emp1)或先声明一下var key;【https://www.cnblogs.com/bqh10086/p/12444757.html,
//但是把代码复制到教程的网页编辑框不会报错:https://www.runoob.com/jsref/jsref-forin.html
//查看老师的教程确实没有先声明,然后运行当时的代码(下面)确实会报错。
//网上也有和老师讲的一样不用关键字,但复制下来运行都有错】https://blog.csdn.net/lx678111/article/details/81239638
//for(key in employee)//老师讲的是,一开始写笔记时候没错,后来出错,详情看上面介绍。
for(let key in employee){
console.log(key);
}
/*
//这个循环自动执行。
name
age
position
signIn
*/
/**/
/*P62-对象-删除属性
//使用delete关键字
var employee={
name:'张三',
age:20,
position:'前端工程师',
signIn:function(){
console.log('张三打卡');
}
}
delete employee.name;
console.log(employee);//{age: 20, position: "前端工程师", signIn: ƒ}
console.log(Object.keys(employee));//(3) ["age", "position", "signIn"]
//学以致用,使用forin
for(key in employee){
console.log(key);
}
/*
//已经没有name这个属性了。
age
position
signIn
*/
/** */
/*P63-对象-构造函数
//使用构造函数是另一个创建对象的方式
//和字面值,new Object()相比,构造函数创建新对象,可以指定默认属性。
//使用new关键字创建对象实例。实例就是根据构造函数创建出来的新对象
//用工厂做比喻,汽车就是实例,工厂就是构造函数,图纸就是原型(Prototype)
//VH:构造函数没有return却可以有返回值的函数。
//构造函数的首字母要大写
//用构造函数构造的实例的属性就是构造函数中这些this定义的属性
//VH:本节中没有在构造函数中定义方法,this.signIn=function(){},这样的方式。在66节原型有例子。
//"哪一个员工来举例子",老师为啥说是一个员工呢?它指这个构造函数还是例子中靠后出现的实例呢?
function Employee(name,position){
this.name=name;//老师这里使用了分号,因为是函数内,不是对象内,虽然是构造函数。
this.position = position;//老师这里使用了分号,因为是函数内,不是对象内,虽然是构造函数。
}
//
var emp1= new Employee('峰华','前端工程师');
console.log(emp1);
var emp2= new Employee('张三','后端工程师');
console.log(emp2);
/*
Employee {name: "峰华", position: "前端工程师"}
name: "峰华"
position: "前端工程师"
__proto__: Object
Employee {name: "张三", position: "后端工程师"}
name: "张三"
position: "后端工程师"
__proto__: Object
*/
/** */
/*P64-this关键字
//this指的是实例对象本身,可以用它访问自身的属性
//构造函数不是实例,但是规定,this.属性这样的语法是定义构造函数创建的实例拥有的属性。
//字面值创建的实例,就很容易理解this
var emp3={
name:'李四',
name2:'李五',
position:'后端工程师',
signIn:function(){
console.log(this.name+'上班打卡');//标号207
},
};
emp3.signIn();//标号207:上班打卡
//后来新增方法,也是可以使用this来获取自身的属性值
//这时this指代的是对象本身emp3
emp3.goToWork = function (){
console.log(this.name2+'去上班');//标号215
};
emp3.goToWork();//标号215:李四去上班
//新增方法的时候,如果使用箭头函数的话,函数体内的this不是指向对象本身,而是window对象
//window对象是浏览器内置的,箭头函数中this指向包裹它的作用域的this对象,
//这里没有包裹它的作用域,所以指向global全局的this,浏览器环境下就是window对象
//如果箭头函数定义在构造函数内部,那么就是构造函数里的this,也就是对象自身
emp3.getOffWork = ()=>{
console.log(this.name+'下班');//标号225
console.log(this);//Window {window: Window, self: Window, document: document, name: "", location: Location, …}
};
emp3.getOffWork();//标号225:下班//没有显示李四
//注意这个例子测试的时候,出现读出name值,或者是undefined下班的情况,
//this.name似乎保留了‘李四’的值,修改对象‘李四1’前面例子的this.name不改变。
//关闭网页,重新打开,就恢复正常了。
/** */
/*在构造函数里使用箭头函数*
//如果箭头函数定义在构造函数内部,那么就是构造函数里的this,也就是对象自身
//注意:箭头函数不适合作为对象方法的定义,一定要使用正常方法function (){}
function Employee(name,position){
this.name=name;
this.position = position;
this.signIn=()=>{
console.log(this.name+'去上班');//标号245
};
}
var emp4=new Employee('王五','前端工程师');
emp4.signIn();//标号245:王五去上班//能够访问员工的名字,
/* */
/*P65-getters和setters
//可以写一些逻辑把值加工后返回出来(或赋给另一个属性)
//VH:经过学习,理解为原有属性为原料的现加工属性(指getter)或现处理属性(指setter),这个属性有自己的名字并不是setter或setter。
//setter的参数只能有一个。set fullname(funllname){},语句中(fullname)参数只能有一个
//只有getter是只读,写了setter就是可以修改。
var person ={
firstName:'三',
lastName:'张',
get fullName(){
return this.lastName+this.firstName;
},
//这里写set fullName(),使fullName有写的属性(能力吧)
set fullName(fullName){//语句中(fullname)参数只能有一个
let [lastName,firstName]=fullName.split(',');
this.lastName = lastName;
this.firstName = firstName;
//上面这么写不会搞错么?哪个是参数,哪个是对象属性?
//带this的是对象属性,不带的就是参数
},
};
//注意这里调用不是函数,不带(),而是像属性一样获取它
console.log(person.fullName);//张三
//写入方式,不是以参数传入函数方式“(参数)”,而是像属性一样直接赋值方式
person.fullName='李,四';
console.log(person.fullName);//李四
console.log(person.lastName,person.firstName);//李 四
//如果是通过构造函数创建的对象,后期也可以给对象添加getter和setter
//Object.definedProperty()
//对象,属性名字,具体函数用{},里面写setter和getter方法,和上面不同。和字面值定义对象的方法很像,但也不同。
//get: function(){}
//set: function(参数名){}
function Employee2(name,position){
this.name=name;
this.position = position;
}
var emp5= new Employee2('赵六','前端工程师');
Object.defineProperty(emp5,'info',{////这里的info是是setter和getter的名字和下面的set:function (info)中的info是函数参数,二者不是一个。
get:function(){
return this.name+' '+this.position;
},
set:function (info){//这里的info是函数参数和上面的emp5后面的info是setter和getter的名字,二者不是一个。
let [name,position]=info.split(' ');
this.name=name;
this.position = position;
}
});
console.log(emp5.info);//赵六 前端工程师
emp5.info='赵七 后端工程师';
console.log(emp5.name);//赵七
console.log(emp5.position);//后端工程师
/** */
/*P66-对象-原型
//JS的对象和函数都有原型。
//原来说过,原型可以想象成图纸
//如果一个函数是构造函数,那么它new出实例就会继承它的原型
//var Employee = function(age,position){//老师是写成function Employee(age,position){}的样式,
function Employee(name,position){
this.name = name;
this.position = position;
this.signIn = function(){
console.log(this.name+'打卡');
};
};
var emp1 = new Employee('张三','前端工程师');
var emp2 = new Employee('李四','后端工程师');
console.log(emp1);//标记16
console.log(emp2);//标记17
//老师说,打开后
//output://标记16的打印结果,标记17类似不再重复
// Employee {name: "张三", position: "前端工程师", signIn: ƒ}
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__: Object//老师说这里就是原型是object,是对象类型的//?原型的类型?有很多种吗?
// //如果再展开,就发现最后一项还有原型,是上级原型,老师说后面小节原型链会讲到
// __proto__:
// constructor: ƒ Employee(name,position)//老师说:目前这个__proto__只有1个(内容),就是这个constructor就是上面定义的构造函数【老师说】3个字出现,表明我并没有理解,哈哈哈
// __proto__: Object//老师说:这里还有一个原型,是上级原型,老师说原型链会讲到
//构造函数的原型增加属性
//如果给原型增加新的属性,那么emp1和emp2都可以继承这个属性
//使用.prototype属性查看构造函数的原型(prototype),//这里老师说的是函数才有prototype属性,原本应该是构造函数,后期消音,有残留,见底部普通函数测试可以使用这个属性(查看和通过它添加新属性)。
//就是上面例子,打印结果中__proto__指向的对象(老师说对象应该是内容的同义词,不是JS中的对象一词)
console.log(Employee.prototype);//{constructor: ƒ}//这样可以直接查看?
//output:与上例中的上级原型是一样的,打印结果如下
// {constructor: ƒ}
// constructor: ƒ Employee(name,position)
// __proto__: Object
//VH:测试打印Employee和它的prototype比较
//打印结果仅仅是函数体,不能通过实例那样点击查看么?
console.log(Employee);//
//output:
// ƒ Employee(name,position){
// this.name = name;
// this.position = position;
// this.signIn = function(){
// console.log(this.name+'打卡');
// };
// }
//给构造函数的prototype(原型)添加新属性和方法的时候,
//它创建的所有的对象,都会自动继承这个属性,无需重新定义emp1和emp2
Employee.prototype.age = 20;//没有就新建,有的话是更新
console.log(emp1.age);//20
console.log(emp2.age);//20
Employee.prototype.printInfo = function(){
console.log(this.name, this.age, this.position);
};
emp1.printInfo();//张三 20 前端工程师
emp2.printInfo();//李四 20 后端工程师
//访问对象的prototype原型
//使用.__proto__的方式
//它的prototype就是构造函数的prototype
console.log(emp1.__proto__);//注意,这里是2个下划线__看上去比较长
//我们可以看到多出age和printInfo,2个属性是自动继承。
//output:打印结果
// {age: 20, printInfo: ƒ, constructor: ƒ}
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
console.log(Employee.prototype);
console.log(emp1.__proto__ === Employee.prototype);
//output:
// {age: 20, printInfo: ƒ, constructor: ƒ}
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
//另一句的结果:
// true//说明2者完全相同
//Object.getProtoTypeOf()查看原型
console.log(Object.getPrototypeOf(emp2));
//output:
// {age: 20, printInfo: ƒ, constructor: ƒ}
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
//VH:测试,一般函数的prototype
//定义一个一般函数
function testProtoTypeOfFunction(){
console.log('测试一般函数的prototype');
}
//开始测试:
//经过测试,对于对象实例来说,Object.getPrototypeOf和.prototype结果一样
//但是,对于函数来说,Object.getPrototypeOf和.prototype结果不一样
console.log(Object.getPrototypeOf(testProtoTypeOfFunction));
console.log(Object.getPrototypeOf(Employee));
console.log(testProtoTypeOfFunction.prototype);
console.log(Employee.prototype);
//前二句的结果:
//ƒ () { [native code] }
//ƒ () { [native code] }
//第三句的结果:
// {constructor: ƒ}
// constructor: ƒ testProtoTypeOfFunction()
// __proto__: Object
//第四句的结果:
// {age: 20, printInfo: ƒ, constructor: ƒ}
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
//VH:测试函数的__proto__和对象的prototype
//结论是老师没有骗人,哈哈哈
console.log(Employee.__proto__);//ƒ () { [native code] }
console.log(testProtoTypeOfFunction.__proto__);//ƒ () { [native code] }
console.log(manager.prototype);//undefined
//VH:下面测试通过prototype给普通函数添加属性,可以添加,普通函数也有prototype属性
//说明,上面老师第一次讲函数.prototype的时候,说得不清楚的地方,应该是后期消音,把“构造”去掉,但留了一些声音
testProtoTypeOfFunction.prototype.age = 35;
console.log(testProtoTypeOfFunction.prototype)
// {age: 35, constructor: ƒ}
// age: 35
// constructor: ƒ testProtoTypeOfFunction()
// __proto__: Object
//【测试结论】函数都原型是个对象,
//里面有2个属性:
//第一个:constructior(值是自己),
//第二个:__prototype__是自己的原型
//综合上面的说法:
//对象实例的原型是构造函数的原型。
/** */
/*P67-对象-Object-create
//Object.create()可以让对象继承另一个对象
//新对象拥有被继承对象的所有属性
//并且还可以用自己特有的属性
//比如人,躯干和四肢,但是都有自己的名字
function Employee(name,position){
this.name = name;
this.position = position;
this.signIn = function(){
console.log(this.name+'打卡');
};
};
var emp1 = new Employee('张三','前端工程师');
var emp2 = new Employee('李四','后端工程师');
Employee.prototype.age = 20;
Employee.prototype.printInfo = function(){
console.log(this.name, this.age, this.position);
};
//直接打印对象没有新增的属性,
//打印结果查看,继承的属性没有打印出来,继承的原型的属性是在原型的层级__prototype__里面
console.log(emp1);//Employee {name: "张三", position: "前端工程师", signIn: ƒ}
//output:
// Employee {name: "张三", position: "前端工程师", signIn: ƒ}
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__://展开
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
//output-end结束
//使用for in即可查看所有的属性(含原型的)
//虽然老师名字声明key,但我遇到报错,
//在之前的课程里面没有要求声明key,详细情况参考P61-对象-遍历
//使用for(let key in emp1),在()中声明key,以后使用这个方式
for(let key in emp1){
console.log(key);
}
//output:
// index.js:26 name
// index.js:26 position
// index.js:26 signIn
// index.js:26 age
// index.js:26 printInfo
//VH:复习一下使用Object.keys,返回数组,也只是本身的属性,没有原型的
console.log(Object.keys(emp1));//(3) ["name", "position", "signIn"]
var manager = Object.create(emp1);
console.log(manager);//本行标记55
//output://标记521
// Employee {}
// __proto__: Employee//展开就是下面内容
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__: Object
//end
//VH:下面是后面学习之后回来的测试,本行标记65【这句话的意思应该是://VH:下面的标记535打印结果是后面增加了标记564和标记565语句后,居然影响到这里。和没有下面2个语句时打印结果标记521不同(复习时增加这句说明)】
//【测试这句时,避免干扰,需要注释后面的manager.name='李四';manager.position='经理';】【需要得到上面的打印结果标记521,需要删除标记564和标记565(复习时增加这句说明)】
//但是这行后面如果有增加了自己的属性的代码,比如 manager.name='李四';【指标记564和标记565这样的句子】
//那么这里的输出会变,也会跟着显示自己的属性name: "李四"【指下面打印结果标记535和打印结果标记521不同,多了name和position属性和值】
//但是,显示的时候第一行,还是Employee{},{}内是空,对比后面的输出Employee {name: "李四", position: "经理"}【标记535,521第一行和标记571的第一行不同】
//output//标记535
// Employee {}
// name: "李四"
// position: "经理"
// __proto__: Employee
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__: Object
//end//【什么原因呢?manager继承自对象emp1,emp1原来有name和postion,继承后,自己重新赋值,那么会造成前面调用这个属性时是自己的新值,不是继承的值】
//VH:下面这行是后面学习之后回来的测试,本行标记81,查看标记118
//如果这行后面如果有增加了自己的属性的代码,比如 manager.name='李四';
//这里使用下面语句,依然是返回[],表示无自己的属性
//console.log(Object.getOwnPropertyNames(manager));//[]//表示无自己的属性//标记83
for(let key in manager){
console.log(key);
}
//output:
// index.js:62 name
// index.js:62 position
// index.js:62 signIn
// index.js:62 age
// index.js:62 printInfo
//end
console.log(manager.name);//张三
manager.name='李四';//标记564
manager.position='经理';//标记565
//现在展开已经发现有自己的元素了。原型里的元素还在。
//现在查看上面没有添加自己元素前的打印console.log(manager)标记65和标记55,虽然也有了自己的元素,但是区别在第一行的显示
//还是Employee{},{}内是空,对比这里的输出Employee {name: "李四", position: "经理"}
console.log(manager);
//outputs//标记571
// Employee {name: "李四", position: "经理"}
// name: "李四"
// position: "经理"
// __proto__: Employee//展开后结果如下
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__: Object
//end
//getOwnPropertyNames()查看自己的属性,另外有测试,查看标记83,本行标记118
console.log(Object.getOwnPropertyNames(manager));//(2) ["name", "position"]
//从这里开始到本节结束,全部是自己测试的代码//
//VH:展开manager的属性
//分析得到如下结构,就好像是背书,前手就是原型,前前手就是原型的原型。
// Employee {name: "李四", position: "经理"}
// 继 name: "李四"
// 承 position: "经理"
// 实 __proto__: Employee
// 例
// 第 name: "张三"
// 一 position: "前端工程师"
// 实 signIn: ƒ ()
// 例 __proto__:
// 构 age: 20
// 造 printInfo: ƒ ()
// 函 constructor: ƒ Employee(name,position)
// 数 __proto__: Object
//end
//测试manager后后手访问前前手的属性,可以直接访问
console.log(emp1.age);//20
console.log(manager.age);//20
//那么我们测试模仿
emp1.age = 25;
console.log(emp1.age);//25
//这时打印emp1发现,和manager增加自己属性一样,age:25出现在了emp1的属性里
console.log(emp1);//20
//output
// Employee {name: "张三", position: "前端工程师", age: 25, signIn: ƒ}
// age: 25
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__:
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
//这时打印manager
//可见前手中出现age:25了。
//自己也继承了前手的age的值,而不是最初构造函数的age:20。
console.log(manager);
console.log(manager.age);//25
//output
// Employee {name: "李四", position: "经理"}
// name: "李四"
// position: "经理"
// __proto__: Employee
// age: 25
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__:
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
//end
//
emp1.printInfo();//张三 25 前端工程师//index.js:19,这个就有点意思。是语句所在行
manager.printInfo();//李四 25 经理//index.js:19
//那么如果跳过emp1,修改printInfo(),那么emp1会如何呢?
manager.printInfo = function(){
console.log(this.name,this.age,this.position,'我是manager.printInfo新定义');
};
emp1.printInfo();//张三 25 前端工程师//index.js:19,emp1.printInfo()的输出没有变
manager.printInfo();//李四 25 经理 我是manager.printInfo新定义//index.js:183 //语句行已经改变
//VH:查看属性列表中各个项目的来源,方式是不一样的。
//老师说只有构造函数有这个prototype(因为它是最初,不可能通过更新原型的age获得自己的age)
//Employee.prototype是打印出来自己的属性,其他的,通过__proto__都是原型的
//上节老师说,构造函数Employee.prototype是自己的原型,字面也是如此,但是我感觉它的原型不应该是__proto__(再往上应该是顶端Obejct对象吧,猜!),但是上节有测试:
//console.log(Employee.__proto__);//ƒ () { [native code] },所以大概是个特殊的规定吧。因为构造函数式对象的开始。
console.log(manager);
//output
// Employee {name: "李四", position: "经理", printInfo: ƒ}
// name: "李四" //后期更新前手的属性增加
// position: "经理" //后期更新前手的属性增加
// printInfo: ƒ () //后期更新前前手属性增加
// __proto__: Employee
// age: 25 //后期更新前手属性增加
// name: "张三" //构造函数本体内定义后new+参数生成的属性
// position: "前端工程师" //构造函数本体内定义后new+参数生成的属性
// signIn: ƒ () //构造函数本体内定义后new生成的属性
// __proto__:
// age: 20 //后期语句增加,老师说只有构造函数有这个prototype(因为它是最初,不可能通过更新原型的age获得自己的age),Employee.prototype.age = 20;
// printInfo: ƒ () //后期语句添加,老师说只有构造函数有这个prototype(Employee.prototype是自己的属性,其他的__proto__都是原型的),Employee.prototype.printInfo = function(){};
// constructor: ƒ Employee(name,position)//最初只有一个属性:构造函数本体
// __proto__: Object
//end
console.log(manager.__proto__.name);//张三
manager.__proto__.__proto__.printInfo();//undefined 20 undefined
//因为构造函数的属性后来只增加一个age:20,和printInfo(),
//printInfo里面打印this.name和this.positon,这name和position自己没有,所以是undefined
//不知道平时调用Employ.printInfo()是否可以,测试不可以,提示不是一个函数,但是通过__proto__层层追溯到方式却可以
//Employee.printInfo();//Uncaught TypeError: Employee.printInfo is not a function
//【比方】我没结婚的时候,我和父母一起生活,爸爸有台电脑。emp1.signIn=function(){(console.log(CPU:I5);toDo('北京欢迎你');}
//有人来访问,张恒借你的电脑我用一下,是不是用的其实是父亲的电脑,manager.signIn(),打印出来CPU:I5,听歌
//后来我买了新电脑,manager.signIn()=function(){console.log(CPU:I7);toDo('王者荣耀');}
//有小朋友又来借电脑玩,manager.signIn(),结果是什么呢?打印出CPU:I7,玩王者
//那么这时候,小朋友说我想听歌,歌只存在爸爸电脑上,怎么办?
//直接baba.signIn(),或者manager.__proto__.signIn()即可。
console.log(emp1.__proto__ === Employee.prototype);//true
console.log(Employee.prototype);
//output:第一行直接{}【第一代:构造函数】
// {age: 20, printInfo: ƒ, constructor: ƒ}
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
console.log(emp1.__proto__);
//output:第一行直接{}【第二代:实例】
// {age: 20, printInfo: ƒ, constructor: ƒ}
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
console.log(manager.__proto__);
//output:第一行Employee {}开头,构造函数开头【第三代:实例继承实例】
// Employee {name: "张三", position: "前端工程师", age: 25, signIn: ƒ}
// age: 25
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__:
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
/** */
/*P68-对象-原型链
//原型链是说,每个对象的原型都还会有一个上层的原型,直到遇到null,这种链式继承下来的原型就是原型链
//JS中最顶层的对象是Object,它的原型是Object.prototype,它的原型的原型就是null,达到原型链的顶端
//VH:我认为函数.prototype(函数特有的.prototype,上句老师说顶级对象Object也有.prototype)其实是自己的属性,特殊规定叫自己的原型。
//VH:因为对于普通对象来说,.__proto__看到的绝对不是自己的属性。而函数和顶级对象Obejct使用.prototype看到的是自己的属性,其中包括__proto__,这个才是真的原型。
//VH:不过我们暂且可以这么说函数和顶级对象Obejct使用.prototype看到的是自己的原型,这是特殊规定
//复制上节代码为本节做铺垫,因为后期自己写了一些代码测试,所以,这里复制干净代码,保持效果和老师一致
//注意上节的代码需要注释掉,一般会注释,不过刚忘记,这就有提示下行有错误://Uncaught SyntaxError: Identifier 'Employee' has already been declared
function Employee(name,position){
this.name = name;
this.position = position;
this.signIn = function(){
console.log(this.name+'打卡');
};
};
var emp1 = new Employee('张三','前端工程师');
var emp2 = new Employee('李四','后端工程师');
Employee.prototype.age = 20;
Employee.prototype.printInfo = function(){
console.log(this.name, this.age, this.position);
};
var manager = Object.create(emp1);
console.log(manager);
for(let key in manager){
console.log(key);
}
console.log(manager.name);//张三
manager.name='李四';
manager.position='经理';
manager.signIn();
console.log(Object.getOwnPropertyNames(manager));
//manager的原型
console.clear();
//VH:首先打印自己manager,就能清楚看到这个链
console.log(manager);
//VH:manager的原型,是emp1的属性
var protoOfManager = Object.getPrototypeOf(manager);
console.log(protoOfManager);
//VH:emp1的原型,是构造函数的属性
var protoOfEmp1 = Object.getPrototypeOf(protoOfManager);
console.log(protoOfEmp1);
//VH:构造函数的原型,是Object的属性【不是.prototype得到的内容】
var protoOfEmp = Object.getPrototypeOf(protoOfEmp1);
console.log(protoOfEmp);
//VH:Object的原型,是null
var protoOfObj = Object.getPrototypeOf(protoOfEmp);
console.log(protoOfObj);
//直接查看
console.log(Object.getPrototypeOf(Object.prototype));
//output:
// Employee {name: "李四", position: "经理"}
// name: "李四"
// position: "经理"
// __proto__: Employee
//
// Employee {name: "张三", position: "前端工程师", signIn: ƒ}
// name: "张三"
// position: "前端工程师"
// signIn: ƒ ()
// __proto__: Object
//
// {age: 20, printInfo: ƒ, constructor: ƒ}
// age: 20
// printInfo: ƒ ()
// constructor: ƒ Employee(name,position)
// __proto__: Object
//
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
// constructor: ƒ Object()
// hasOwnProperty: ƒ hasOwnProperty()
// isPrototypeOf: ƒ isPrototypeOf()
// propertyIsEnumerable: ƒ propertyIsEnumerable()
// toLocaleString: ƒ toLocaleString()
// toString: ƒ toString()
// valueOf: ƒ valueOf()
// __defineGetter__: ƒ __defineGetter__()
// __defineSetter__: ƒ __defineSetter__()
// __lookupGetter__: ƒ __lookupGetter__()
// __lookupSetter__: ƒ __lookupSetter__()
// get __proto__: ƒ __proto__()
// set __proto__: ƒ __proto__()
//
// null
//
// null
//Object.prototype中有一个toString()方法,可以传递到原型链的最底端,用manager去使用它。
//toString()方法是查看对象的类型
console.log(manager.toString);//ƒ toString() { [native code] }
console.log(manager.toString());//[object Object]
console.log(Object.toString());//function Object() { [native code] }
console.log(Employee.toString());
console.log(Employee);
//output
// function Employee(name,position){
// this.name = name;
// this.position = position;
// this.signIn = function(){
// console.log(this.name+'打卡');
// };
// }
// ƒ Employee(name,position){
// this.name = name;
// this.position = position;
// this.signIn = function(){
// console.log(this.name+'打卡');
// };
// }
//end
//大测试
console.log(emp1);//对象
console.log(protoOfManager===emp1);//true
//测试开始:
console.log(Employee);//函数体//并不是的对象,加()就是运行,只能使用Employee.prototype达到效果
console.log(Employee.prototype);//函数的.prototype(属性),对象结构
console.log(protoOfEmp1===Employee.prototype);//true
console.log(Object.getPrototypeOf(Employee));//ƒ () { [native code] }//我们能用
//测试开始:
console.log(Object);//ƒ Object() { [native code] }
console.log(Object.prototype);//对象类型//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log(protoOfEmp===Object.prototype);//true
//还有个查看自己属性的方法可以测试
console.log(Object.getOwnPropertyNames(manager));//(2) ["name", "position"]
console.log(Object.getOwnPropertyNames(emp1));//(3) ["name", "position", "signIn"]
console.log(Object.getOwnPropertyNames(Employee));//(3) ["length", "name", "prototype"]
console.log(Object.getOwnPropertyNames(Object));//(24) ["length", "name", "prototype", "assign", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors", "getOwnPropertyNames", "getOwnPropertySymbols", "is", "preventExtensions", "seal", "create", "defineProperties", "defineProperty", "freeze", "getPrototypeOf", "setPrototypeOf", "isExtensible", "isFrozen", "isSealed", "keys", "entries", "fromEntries", "values"]
// 原型__proto__和原型对象prototype,
// 网友也叫隐式原型,和显式原型。
// 一般来说,对象都有隐式原型,形成原型链。
// 函数才有prototype,显式原型(函数也是对象,最后再说)
// 原型是指实例的__proto__指向的父级的对象(也可能是实例)
// 原型对象指的是函数的样品实例默认的样品。也是个对象。
// 构造函数在使用的时候,自己也是对象,但是自己在原型链的时候,是自己的原型对象来参与的。所以,
// 我们在使用Object.getPrototypeOf()的时候,得到的是构造函数.prototype(这个属性值是对象类型的)
// 而manager继承自emp1,两个都是对象,所以,
// manager的原型是emp1,
// 但emp1的原型是构造函数.prototype的值(假如是A,对象类型)
// 那这个A的原型是Object.prototype的值(加入是b,b也是对象类型)
// 如果作为对象,
// 构造函数的对象属性有哪些呢?我们能看到(3) ["length", "name", "prototype"],其中是有prototype属性的。
// 所以,在原型链中,到构造函数这里,就是使用它.prototype
// 原型都是对象,构造函数这里是函数,不是对象。它是使用自己的一个样品对象来给人家继承的。不是自己。
console.log(Object.getOwnPropertyNames(Employee));//(3) ["length", "name", "prototype"]
// 构造函数作为对象原型是啥就是用__proto__来查看。
// 或者是使用
console.log(Employee.__proto__);//ƒ () { [native code] }
console.log(Object.getPrototypeOf(Employee));//ƒ () { [native code] }
//这里有个比喻,
//函数是马良,它照着女儿画了一幅画,变成了一个女孩。女孩生了个女孩。
//巫婆,继承的不是马良,而是马良的女儿,
//马良的女儿继承自Object.女儿。
//女娲捏了一个女孩给马良当媳妇,马良照着媳妇画了一个女人,女人生女儿,女儿生女儿。
///,画了画变成仙女女儿,给了马良当女儿,马良照着女儿画了画,变成了人女孩1,女孩又生人女儿2,女儿又生人女儿3。
//我说画出来,意思是仿照的来,不是生出来。
//我仿照的对象是你生出的吗?不是是我仿照出来的。仿照谁呢?
//许愿池null0,神仙想要老伴,从许愿池走出来一位老伴,objcet.老伴1来自许愿池
//老神仙看马良不错,照着自己的老伴画了一副画变成了人给马良做媳妇2,马良.老伴来自老神仙.老伴
//马良照着自己的媳妇画了一幅画,成了漂亮女子西施3,西施来自于马良.老伴
//西施3生了女儿,东施4,
//那么神仙,马良作为实例,怎么来的呢?这些怎么来的呢?
//console.log()的作用是打印本体对象出本体,能直接查看__proto__,函数不带()的话出定义,带()就是运行,打印返回值而已,不会显示作为对象的结果。
console.log(Object);//ƒ Object() { [native code] }
console.log(Employee);//函数体//并不是的对象,加()就是运行,只能使用Employee.prototype达到效果
//自己作为实例的原型是什么?
console.log(Object.getPrototypeOf(Employee));//ƒ () { [native code] }
console.log(Object.getPrototypeOf(Object));//ƒ () { [native code] }
console.log(Employee.__proto__);//ƒ () { [native code] }
console.log(Object.__proto__);//ƒ () { [native code] }
//网友:__proto__,指向构造函数的原型对象
// 构造函数Foo()除了是方法,也是对象啊,它也有__proto__属性,指向谁呢?
// 指向它的构造函数的原型对象呗。函数的构造函数不就是Function嘛,因此这里的__proto__指向了Function.prototype。
console.log(Function.prototype);//ƒ () { [native code] }
console.log(Object.constructor);//ƒ Function() { [native code] }
//那么这是啥意思呢?就是?
// 就是说,如果把函数当做对象,那么它的也有构造函数,Function,,funtion根据自己的媳妇画了一个女子,那么这个人是马良(Employee)吧。
// 神仙画师算什么?它是函数么?
// function的媳妇从哪里来的?object.prototype。老画师的媳妇来的么?
// function从哪里来的呢?它是function,这是函数还是对象呢?、
// 给这些作为对象人添加属性和方法可以么?
// 其他一些思考:
// 原型__proto__和原型对象prototype,
// 网友也叫隐式原型,和显式原型。
// 一般来说,对象都有隐式原型,形成原型链。
// 函数才有prototype,显式原型(函数也是对象,最后再说)
// 原型是指实例的__proto__指向的父级的对象(也可能是实例)
// 原型对象指的是函数的样品实例默认的样品。也是个对象。
// 构造函数在使用的时候,自己也是对象,但是自己在原型链的时候,是自己的原型对象来参与的。所以,
// 我们在使用Object.getPrototypeOf()的时候,得到的是构造函数.prototype(这个属性值是对象类型的)
// 而manager继承自emp1,两个都是对象,所以,
// manager的原型是emp1,
// 但emp1的原型是构造函数.prototype的值(假如是A,对象类型)
// 那这个A的原型是Object.prototype的值(加入是b,b也是对象类型)
// 如果作为对象,
// 构造函数的对象属性有哪些呢?
// 构造函数作为对象原型是啥就是用__proto__来查看。
/** */
/*P69-对象-修改原型指向
//通过修改原型指向,改变它的继承关系
//复制上节必要代码
function Employee(name,position){
this.name = name;
this.position = position;
this.signIn = function(){
console.log(this.name+'打卡');
};
};
var emp1 = new Employee('张三','前端工程师');
Employee.prototype.age = 20;
Employee.prototype.printInfo = function(){
console.log(this.name, this.age, this.position);
};
var manager = Object.create(emp1);
manager.name='李四';
manager.position='经理';
//定义一个名叫Manager的构造函数
function Manager(){
}
//给Manager的prototype添加新的属性
Manager.prototype.department = '技术部';
//改变manager的prototype给改变一下,
Object.setPrototypeOf(manager,Manager.prototype);
//这样manager就有了构造函数Manager.prototype.department的属性
console.log(manager.department);//技术部
//查看原型,只有department一个属性,然后它就变成了Manager这个构造函数了
console.log(Object.getPrototypeOf(manager));//{department: "技术部", constructor: ƒ}
//再访问emp1里面的方法,就访问不到了。
//console.log(manager.signIn());//manager.signIn is not a function
//循环遍历一下它的属性,看到只有name,position,department
for(let key in manager){
console.log(key);
}
//output
// name //是manager之前定义的自己的属性,不是继承的
// position //是manager之前定义的自己的属性,不是继承的
// department //是它的构造函数.prototype里面定义的,
//是这个也不是自己的吧,(为啥会出现呢?)这个是Manger.prototype的
//end
//所以,改了原型之后,和Employee就没有任何关系了。
//通过这个方式,可以把对象的继承关系给改变,让它继承自其他东西
//另外利用原型prototype,一大用途,给现有的Object添加一些额外的属性
//然后让它所有的对象都能够使用,比如,JS内置对象Array,它有一些forEach,map之类的方法
//咱们通过可以改变Array.prototype给它添加一些新的属性或者方法进去,
//那么只要是数组,它都可以方便访问到咱们新添加的属性或方法
//这就可以用来做一些扩展,就相当于其他编程语言的extention这样的语法
//大家可以根据自己的实际情况去使用它
//另一种就是继承关系,一些对象要继承另一些对象的属性,咱们可以不用重复定义
//直接用它prototype里继承下来的方法就可以了。
//比如object里面的toString,咱们在底下所有的对象中都可以使用
//咱们也可以自己定义这些属性,让继承者都可以使用这些属性
/** */
/*70-对象-Spread操作符
//和rest操作符写法一样,使用三个点...
//用来把对象的属性,或者数组的元素进行分离,或者是扩展为单个项目
//与rest操作符相反
//比如我可以用它来克隆出一个对象
//咱们这里定义一个对象
var post = {
id:1,
title:'标题1',
content:'这是内容',
};
console.log(post);//{id: 1, title: "标题1", content: "这是内容"}
//我们使用对象post来克隆一个新对象
//使用spread操作符,把它的属性,1个1个都拿出来,放到一个新的对象里面
var postClone = {... post};
console.log(postClone);//{id: 1, title: "标题1", content: "这是内容"}
//值是一样的,但是它们是两个不同的对象,内存地址不一样,两个不同的引用
console.log(post===postClone);//false
//基于一个对象添加新的属性
var post2 = {
...post,
author:'峰华',
};
//通过查看,新的属性author:'峰华',已经被添加进来
console.log(post2);//{id: 1, titile: "标题1", content: "这是内容", author: "峰华"}
//Spread操作符也可以用在数组中,
//咱们可以创建一个数组的克隆,也可以基于它创建一个有额外元素的新数组
var arr =[1,2,3];
var arrClone = [...arr];
console.log(arrClone);//(3) [1, 2, 3]
//添加新属性,操作跟之前的对对象进行的操作差不多
var arr2= [... arr,4,5,6];
console.log(arr2);//(6) [1, 2, 3, 4, 5, 6]
//世界上有那么多人在做着‘傻事’但是热爱的事,我也添一份力量,力所能及。
//另外在函数里面,如果一个函数接收多个参数,
//可以通过数组spreaad的方式传递进去
//定义一个函数保存博文,它需要3个参数,然后简单模拟对参数的操作,这里用打印出来的方式
function savePost(id,title,content){
console.log('保存了:',id,title,content);
}
//调用savePost(),我们可以是使用...[]的样式
//spread操作符可以把数组分解为3个参数传给savePost(),
savePost(...[2,'标题','内容内容']);//保存了: 1 2 3
/** */
/*P71-对象-destructing和rest
//数组那节,我们介绍了destructing和signMent结构赋值
//还有rest操作符
//它们也可以用在对象上面
//destructing和assignMent
var post = {
id:1,
title:'标题1',
content:'这是内容',
};
//{}中的变量名,需要和对象中的属性名称一致
var {id, title} = post;
console.log(id,title);//1 "标题1"
//别名,使用冒号接在变量名后面
var {id, title: headline} = post;
console.log(id,headline);//1 "标题1"
//定义默认值
//防止无此属性,或值为undefined,
//但是如果值是null,就不会被默认值替代,因为null本身是值
var {id, title, comments = '没有评论'} = post;
console.log(comments);//没有评论
//测试有属性但值为null
var post = {
id:1,
title:'标题1',
content:'这是内容',
comments:null,//增加comments属性,这里相较于上面的例子用的post对象,
};
var {id, title, comments = '没有评论'} = post;
console.log(comments);//null
//数组解构赋值,也可以有默认值,VH:下例是没有对应元素使用默认值的情况,数组估计不会出现没有对应属性的情况
var [a,b = 2]=[1];
console.log(a,b);//1 2
//结合数组和对象的解构赋值,可以解构复杂的对象结构
//比如下面,我只想取comments属性中的第二组中的coment的值
var post2 = {
id:2,
title:'标题2',
content:'这是内容',
comments:[
{
userId:1,
comment:'评论1',
},
{
userId:2,
comment:'评论2',
},
{
userId:3,
comment:'评论3',
},
],
};
var{comments:[,{comment}]} = post2;
console.log(comment);//评论2
//觉得漫长是因为难过,是因为求而不得。是因为自己资历不够!
//杭州的日子非常的快!!!上天也是眷顾我!
//解构的时候,可能key是动态的,是个变量。
//可以用方括号把变量名括起来,作为key//是不是不能用变量的值当做另一个变量名
//VH:经过后面测试,必须使用[]和别名的方式!
function getId(idKey,obj){
let{[idKey]: id } = obj;
return id;
}
console.log(getId('userId',{userId:3}));//3
//测试,这就很奇怪!1.为啥用[]2.为啥用别名?
//不使用[],语句标记650:Uncaught SyntaxError: Identifier 'idKey' has already been declared
//因为和参数中的idKey重复了。参数的idKey改成idKey1就没有错误,但是结果undefined
//为啥不行呢?之前的时候参数在函数里的作用是作为变量,这里是作为变量名,所以,let{[idKey]: id } = obj;
// function getId(idKey,obj){
// let{idKey} = obj;//标记650
// return idKey;
// }
// console.log(getId('userId',{userId:3}));//3
//那么不用别名行不行?不行。写完,当时VScode就在let{[idKey]}末尾处下划波浪线提示有问题,运行报错//Uncaught SyntaxError: Unexpected token '}'
// function getId(idKey,obj){
// let{[idKey]} = obj;//Uncaught SyntaxError: Unexpected token '}'
// return [idKey];
// }
// console.log(getId('userId',{userId:3}));//3
//rest操作和数组一样
//剩余的可以当做子对象返回回来
var {comments, ...rest} = post2;
console.log(rest);//{id: 2, title: "标题2", content: "这是内容"}
console.log(post2);//原对象未受影响
//VH:上面那样复杂点的呢?结果和上面一样,只认第一层的
var{comments:[,{comment}],...rest} = post2;
console.log(rest);//{id: 2, title: "标题2", content: "这是内容"}
//解构和rest操作符还可以作为函数到参数,
//结构操作让对象元素直接作为参数
function savePostObj({id, title, content}){
console.log('保存了文章:', id, title, content);//标记674
}
savePostObj({id: 4, title:'标题4', content:'内容4'});//index.js:674保存了文章: 4 标题4 内容4
//添加...rest,函数体内使用rest,这时savePostObj改成savePostObj2
function savePostObj2({id, title, content, ...rest }){
console.log('保存了文章:', id, title, content);//标记684
console.log(rest);
}
savePostObj2({id: 4, title:'标题4', content:'内容4',author:'峰华'});//index.js:684 保存了文章: 4 标题4 内容4
//output:
//保存了文章: 4 标题4 内容4
//{author: "峰华"}
// author: "峰华"
// __proto__: Object
//end
/** */
/*P72-对象-值传递和引用传递
//Array和Object在函数的参数中是按引用传递的
//也就是说它传递的是内存地址,咱们修改它的值之后,所有用到该地址的值都会变化
//不过,数字和布尔等基本类型是按值传递的,也就是说它的值会被复制一份新的
//在函数中修改它的值之后,不会影响它之前的值
//例子:按引用传递
//=>数组
function byReference(arr){
arr[0] = 5;
}
var array = [1,2,3];
byReference(array);
console.log(array);//(3) [5, 2, 3]
//=>对象:post对象的title是引用位置,改动1处处处调用变动,变成了‘标题标题’
function byReferenceObj(obj){
obj.title = '标题标题';
}
var post ={id:1,title:'标题'};
byReferenceObj(post);
console.log(post);//{id: 1, title: "标题标题"}
//例子:按值传递
//=>字符串:字符串比较特殊,虽然是按引用传递的,但是它每次被赋值的时候,都是创建一个新的string对象,旧的不会改变。
//VH:字符串作为参数,并没有被 str= 'abc';赋值为'abc' str= 'abc'还是自己的值'test'。
function byReferenceStr(str){
str= 'abc';//运行到这里,创建了一个新的对象,没有改变testStr
console.log(str);//abc
}
var testStr = 'test';
byReferenceStr(testStr);
console.log(testStr);//test
//=》
function byValue(num){
num = 10;
console.log(num);//10
}
var testNum =1;
byValue(testNum);
console.log(testNum);//1//说明函数复制一份testNum来使用,没有改变外部testNum的值。
/** */
/*P73-对象-call&apply&bind
//call, apply 和bind都可以用来改变函数中this的指向,
//不过它们有一些细微的差别
console.clear();
//比如我这里定义一个员工对象
//emp对象的printInfo()方法中的this.name指的是emp的name属性
//属性department的属性又有对象,那么department中的printInfo中this.name指的是department的name
var emp = {
id:1,
name:'峰华',
printInfo(){
console.log('员工姓名:'+this.name);
},
department:{
name:'技术部',
printInfo(){
console.log('部门名称:'+this.name);
}
}
};
emp.printInfo();//员工姓名:峰华
emp.department.printInfo();//部门名称:技术部
//其实printInfo()中的this指向的是左边的对象,第一个左边是emp,第二个左边是departmnet
//以上this是默认绑定的,我们不需要特别的操作
/** */
/*call的用法
//如果我把printInfo单独拿出来,
var emp = {
id:1,
name:'峰华',
};
function printInfo(){
console.log('员工姓名:'+this.name);//标记774
}
//printInfo()直接调用,其中this指向window对象,它里面没有name这个属性
//VH:因为我测试调用就错误,所以只能//注释下面这行
//VH:注意,老师课堂视频中,打印结果没有报错:【员工姓名:】,估计老师vscode中是上面某个地方有name变量,但是值为空
// printInfo();//标记778
//output
// Uncaught TypeError: Cannot read property 'name' of undefined
// at printInfo (index.js:774)
// at index.js:778
//end
//使用call方法,显式地给printInfo绑定一个this指向
printInfo.call(emp);//员工姓名:峰华(index.js:774)
/** */
/*call,apply的区别,bind方法
//带参数的函数使用call和apply
var emp = {
id:1,
name:'峰华',
};
//给printInfo添加额外的参数
function printInfo(dep1,dep2,dep3){
console.log('员工姓名:'+this.name, dep1,dep2,dep3);//标记774
}
//call使用时,第一个参数是需要绑定的对象,后续参数依次就是函数定义时定义的参数
printInfo.call(emp, '技术部','IT事业部','总裁办公室');//员工姓名:峰华 技术部 IT事业部 总裁办公室
//apply可以使用数组形式的参数,一次把参数传递进去,数组内的元素顺序与函数定义时次序一致
printInfo.apply(emp,['技术部','IT事业部','总裁办公室']);//员工姓名:峰华 技术部 IT事业部 总裁办公室
console.clear;
//bind操作与call一样,需要参数列表,
//不同的是而是返回一个改变了this指向的新函数,不会马上执行,用于后续执行
//可以用变量接收这个新函数,在后面调用
var empPrintInfo = printInfo.bind(emp,'技术部','IT事业部','总裁办公室');
empPrintInfo();//员工姓名:峰华 技术部 IT事业部 总裁办公室
/** */