数组实际上也是一个对象
继承
1. 原型链继承
Professor.prototype = {
name:'Mr.Zhang',
tSkill:'JAVA'
}
function Professor(){}
var professor = new Professor();
Teacher.prototype = professor;
function Teacher(){
this.name = 'Mr.Wang',
this.mSkill = 'JS/JQ'
}
var teacher = new Teacher();
Student.prototype = teacher;
function Student(){
this.name = 'Mr.Li',
this.pSkill = 'HTML/CSS'
}
var student = new Student();
console.log(student);
思考:
学生需不需要继承老师和教授身上的所有属性?
像名字这种是没必要继承的
所以原型链这种不友好 -->尝试用call和apply来解决问题
2. call、apply
function Teacher(name,mSkill){
this.name = name;
this.mSkill = mSkill;
}
function Student(name,mSkill,age,major){
// 我们这里要用到teacher的属性
Teacher.apply(this,[name,mSkill]);
this.age = age;
this.major = major;
}
var student = new Student('lili','js','13','Computor');
console.log(student);
- 借用别人的属性和方法的时候,使用这种方法来做
- 这种方法没有办法继承Teacher的原型
- 这种也不算继承
3. 组合继承
存在问题:构造函数复用
function Teacher(name,mSkill){
this.name = name;
this.mSkill = mSkill;
}
var t = new Teacher();
Student.prototype = t;
function Student(name,mSkill,age,major){
// 我们这里要用到teacher的属性
Teacher.apply(this,[name,mSkill]);
this.age = age;
this.major = major;
}
var student = new Student('lili','js','13','Computor');
console.log(student);
4. 寄生组合继承(经典继承)
5. 仅继承原型
function Teacher(){
this.name = 'Mr.Li';
this.tSkill = 'JAVA';
}
Teacher.prototype = {
pSkill:'JS/JQ'
}
var t = new Teacher();
console.log(t);
function Student(){
this.name = 'Mr.Wang';
}
//要注意这两句的顺序!
//这样就继承了Teacher的原型,没继承不需要的内容
Student.prototype = Teacher.prototype;
// 这个时候想在学生原型上加点属性
//但是这样,Teacher.prototype也添加上了这个属性
//因为他们是相等的,他们指向同一个原型对象,你改我也改
Student.prototype.age = 18;
var s = new Student();
console.log(s);
Student.prototype = Teacher.prototype;
因为他们是相等的,他们指向同一个原型对象,当你想在学生原型上加点属性时,你改我也改
4. 企业级继承和隔离—— 圣杯模式
借用一个中间量来中转一下,希望它是一个对象,而且拥有Teacher的原型,也就是要有一个中间的构造函数
这个构造函数实例化了以后,接收Teacher的prototype,然后再把Student.prototype等于这个中间的实例化出来的对象
这样Sdudent.prototype 与 Teacher.prototype 不是同级的
function Teacher(){
this.name = 'Mr.Li';
this.tSkill = 'JAVA';
}
Teacher.prototype = {
pSkill:'JS/JQ'
}
var t = new Teacher();
console.log(t);
function Student(){
this.name = 'Mr.Wang';
}
function Buffer(){}
Buffer.prototype = Teacher.prototype;
var buffer = new Buffer();
console.log(buffer);
Student.prototype = buffer;
var s = new Student();
Student.prototype.age = 18;
console.log(s);
封装圣杯
function Teacher(){}
function Student(){}
// // function Buffer(){}
// Buffer.prototype = Teacher.prototype;
// var buffer = new Buffer();
// Student.prototype = buffer;
inherit(Student,Teacher);
var s = new Student();
var t = new Teacher();
console.log(s);
console.log(t);
function inherit(Target,Origin){
function Buffer(){}
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer();
//被继承的一方叫做超类
//防止构造器指向紊乱(不是系统紊乱,而是你记忆紊乱),写一下更清晰
Target.prototype.constructor = Target;
Target.prototype.super_class = Origin;
}
CSS圣杯模式 双飞翼
构造函数也是一个闭包,在new的时候隐式的return了this
如果你想覆盖这个隐式返回:
你返回的是原始值,它不认 但是!你返回引用值的话就成功了
怎么样能够把圣杯模式像闭包的形式 包装一下
var inherit = (function (){
var Buffer = function(){};
function inherit(Target,Origin){
Buffer.prototype = Origin.prototype;
Target.prototype = new Buffer();
//被继承的一方叫做超类
//防止构造器指向紊乱(不是系统紊乱,而是你记忆紊乱),写一下更清晰
Target.prototype.constructor = Target;
Target.prototype.super_class = Origin;
}
return inherit;
})();
function Teacher(){}
function Student(){}
inherit(Student,Teacher);
var s = new Student();
var t = new Teacher();
console.log(s);
console.log(t);
出现报错情况,把立即执行函数放在了方法调用之后,虽然是立即执行函数会立即执行,但是你执行完之后赋值给了变量,就不会继续再执行了,你调用变量的话,当然是按顺序执行,所以要写在调用之前。
ES6 extends 继承:
企业级模块化
- 在立即执行函数里写,相当于一个小的独立的全局
- 名称前加个 init前缀⑧
- 不同的模块 最后都放到 init()里来初始化
作业:
打印一个参数以内能被3/5/7 整除的数
打印斐波那契数列的第N位
打印从0到一个数的累加值
各自写各自的功能,然后整合在一个js里边
访问原型的两种方式:
- 通过构造函数.prototype
- 通过实例化对象.__proto __
13时
1. 链式调用
现在执行会报错,那么怎么样让它全部能够执行出来?
var sched = {
wakeup:function(){
console.log('running');
},
morning:function(){
console.log('go shopping');
},
noon:function(){
console.log('have a rest');
},
afternoon:function(){
console.log('learning');
},
evening:function(){
console.log('walking');
},
night:function(){
console.log('sleeping');
}
}
sched.wakeup().morning().noon().afternoon().evening().night();
每个函数里头都加 return this;
执行完一个方法的时候 返回this(就是把对象return出去) --> 就指回sched
2. this.属性名 = this[ ’ 属性名 ’ ]
3. 枚举–>遍历
在js中 有枚举就有遍历,有遍历就有枚举
4. for in
能够循环遍历对象,也能够循环遍历数组
var car = {
brand:'Benz',
color:'red',
displacement:'3.0',
lang:'5',
width:'2.5'
}
for(var key in car){
// console.log(car.key) 会报错
//因为, car.key ->car['key'] -> undefined
//意思是它不管你是不是变量,
//你点了之后就当成属性转换成car['key'],但是因为你对象里头没有key属性,所以报错
console.log(key + ':' + car[key]);
}
var car = {
brand:'Mazda',
color:'red'
}
console.log('displacement' in car); //false
要通过 in查找car里头有没有displacement属性,记得要加引号
它是判断属性存不存在这个对象上!所以返回的是true / false
5. obj.hasOwnProperty方法
作用:仅找对象自身的属性
它是排除原型的,但是 in 不排除
hasOwnProperty用的比较多,in不怎么用知道就行
3. instanceof (肯定会考的)
作用:判断这个对象是不是该构造函数实例化出来的
▲笔试的时候要答得出:
function Car(){
}
var car = new Car();
console.log(car instanceof Car);
function Person(){}
var p = new Person();
console.log(p instanceof Car); //false
console.log(car instanceof Object); //true
console.log([] instanceof Array); //true
console.log([] instanceof Object); //true
console.log({} instanceof Object); //true
// A对象的原型里到底有没有B的原型? 有
三种判断数组的方法
var a = [1,23] || {}
console.log(a.constructor); //第一种
console.log(a instanceof Array); //第二种
var str = Object.prototype.toString.call(a) // 第三种,这个会经常用到的
if(str === '[object Array]'){
console.log('是数组');
}else{
console.log('不是数组');
}
console.log(str);
console.log(a.toString());
- 第三种方法会经常用到
- 要区分一下 自己.prototype.toString() 和 Object.prototype.toString()
4. callee、caller
(没啥用,在严格模式下还会报错。但是笔试喜欢考这个)
1. 获取形参个数的两种方法:
function test(a,b,c){
console.log(arguments.callee.length); // 3
console.log(test.length); // 3
console.log(arguments.length); // 4
}
test(1,2,4,5);
arguments.callee表示:实参列表所对应的函数是谁,它就返回哪个函数
arguments.callee 你在哪个函数里边就执行哪个函数
function sum(n){
if(n <= 1){
return 1;
}
return n + sum(n - 1);
}
改成立即执行函数的写法
2.caller
它可以打印出 谁调用它(前提是它被调用了)
返回当前被调用函数的函数引用
NaN不等于任何值 就也不等于自己啊!!!
5. this指向、构造函数的函数上下文
题1:
function foo(){
bar.apply(null,arguments);
//没有指向的话,就正常执行。
//相当于 bar(arguments)
}
function bar(){
console.log(arguments);
}
foo(1,2,3,4,5); //打印arguments
题2:
JS的typeof能够返回哪些值?
number、string、boolean、undefined、function、object(null)
它并不打印null!null的时候打印object
题3:
function b(x,y,z){
arguments[2] = 10;
alert(z);
//-----------------
z = 6;
alert(arguments[2]);
}
b(1,2,3); //10
形参和实参是一一对应的映射关系,你改我也改
题4:
var f = (
function f(){
return '1';
},
function g(){
return 2;
}
)
//请问 typeof(f) 打印出来是什么?
// function
//因为 (f(),g()) 逗号返回的是最后一个
- 逗号运算符,
- 要看清后头有没有立即执行 不要因为刷题刷多了惯性思维反应为return的值了!
题5:
console.log(undefined == null); //true
console.log(undefined === null); //false
console.log(isNaN('100')); //false
console.log(parseInt('1a') == 1); //true
题:
空对象等不等于空对象?为什么不等于?怎样才能让他们相等?
答:
- 因为引用值对比的是地址值!两个空对象存储在不同的空间里
- 赋值的方法
var obj = { };
obj1 = obj;
题:
var a = '1';
function test(){
var a = '2';
this.a = '3';
console.log(a); // '2'
}
test(); //2
new test(); // 不打印
console.log(a); // '3'
分析:
var a = '1';
function test(){
var a = '2';
this.a = '3';
console.log(a); // '2'
console.log(this.a);
}
test(); //2
console.log('---------------------');
new test(); //
console.log('---------------------');
console.log(a); // '3'
// GO = {
// a:undefined --> '1',
// test:function test(){},
// }
// AO = {
// this:window
// a:undefined --> 2
// }
// AO = {
// this:{
// a:3
// }
// a:undefined --> 2
// }
错,以为它没被调用所以不打印
它 new的时候实际上就执行了(调用了)
题:
var a = 5;
function test(){
a = 0;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test(); // 0,5,0
console.log('---------------------');
new test(); // 0 undefined 0
分析:
// GO = {
// a:undefined
// 5,
// test:function test(){}
// }
// AO = {
// a:undefined --> 0
// }
// function test(){
// var this = {
//
// }
// a = 0;
// console.log(a);
// console.log(this.a);
// var a;
// console.log(a);
// }
// AO = {
// a:undefined --> 0
// this:{ }
// }
14时
1. 三元运算
条件表达式 ?是 :否
是 否里头的内容
- 可以直接是执行语句 且还能继续嵌套判断
- 它自己可以把结果返回出来(所以通常都会声明一个变量去接)
题:
var str = 89 > 9 ? (
'89' > '9' ? '通过了'
:'内层未通过'
)
: '外层未通过';
console.log(str);
字符串与字符串比较!是从第一位开始比较对应的ASCII码 (笔试的时候千万不能失分啊!)
2. 克隆/复制/拷贝
- 赋值
var person1 = {
name: 'zhangsan',
age: 19,
sex: 'male',
height: 174,
weight: 150
}
var person2 = person1
- clone,声明个空对象,for循环,往里填东西 (这样对象的地址不一样了)
var person2 = {};
for(var key in person1){
person2[key] = person1[key];
}
}
- 引用值的克隆
1.浅拷贝
只处理了第一层的属性,没有处理下面的引用值
对于引用值属性,还是拿了别人的引用空间
注意!
Object.protorype自定义的属性,在for循环时也被放到 person2里了。要剔除出去,只拷贝person1的属性
- 怎么样把原型给剔除出去 - hasOwnProperty
单独的一段for循环代码不好看,将它封装一下,同时剔除原型上的属性
浅拷贝函数:
function clone(origin,target){
//如果用户没有填target参数
var target = target || {};
for(var key in origin){
if(origin.hasOwnProperty(key)){
target[key] = origin[key];
}
}
return target;
}
2. 深拷贝
最后要达到的目的:不管对象里边是引用值还是原始值,我改变了我自己的内容,怎么操作都和你没有关系
有没有传值
首先判断 这个属性是不是我自己的还是原型上的
然后判断这个属性是原始值还是引用值。 是不是 对象类型的 (因为历史遗留问题,所以还要判断下这个属性 !==null
再然后,判断是数组还是对象
// 有引用值的时候要做一系列相关的遍历,
// 判断里头每一项键值对是不是引用值
// 是引用值的话,则判断它是数组还是对象。
//进去后再判断里头每一项键值对是不是引用值,如果是再继续
var person1 = {
name: 'zhangsan',
age: 19,
sex: 'male',
height: 174,
weight: 150,
children: {
first: {
name: 'zhangyi',
age: 13
},
second: {
name: 'zhangsaner',
age: 10
},
third: {
name: 'zhangsanan',
age: 9
}
},
car: ['Benz','Mazda']
}
var person2 = deepClone(person1);
person2.children.forth = {
name:'zhangsajkh',
age: 3
};
person2.car.push('volvo');
console.log(person1,person2);
function deepClone(origin,target){
// 防止用户不传值
var target = target || {},
toStr = Object.prototype.toString,
arrType = '[object Array]';
for(var key in origin){
// 先判断这个属性是不是origin的
if(origin.hasOwnProperty(key)){
// 再判断它是不是引用值 注意! typeof(null)打印出来的也是object
if(typeof(origin[key]) === 'object' && origin[key] !== null){
// 再判断这个引用值是数组还是对象,是数组则创建空数组,对象创建空对象
if(toStr.call(origin[key]) === arrType){
target[key] = [];
}else{
target[key] = {};
}
// 再递归,拷贝引用值
deepClone(origin[key],target[key])
}else{
target[key] = origin[key];
}
}
}
return target;
}
笔试题:
function test(){
console.log(foo); //undefined
var foo = 2;
console.log(foo); //2
console.log(a); //undefined
}
test();
错!!!
console.log(a);会报错
打印出:a is not defined
都没有声明啊!!(这个笔试千万不能错 哭)
function a(){
var test;
test();
function test(){
console.log(1);
}
}
a(); // 1
// a执行的时候 test会被执行
- 这题是关于this指向很难的题哦!
var name = '222';
var a = {
name:'111',
say:function(){
console.log(this.name)
}
}
var fun = a.say;
fun(); // '222'
a.say(); // '111'
var b = {
name:'333',
say:function(fun){
fun();
}
}
b.say(a.say); // '222'
b.say = a.say;
b.say(); // '333'
4.关于this指向
function test(){
var marty = {
name:'marty',
printName:function(){
console.log(this.name);
}
}
var test1 = {
name:'test1'
}
var test2 = {
name:'test2'
}
var test3 = {
name:'test3'
}
test3.printName = marty.printName;
marty.printName.call(test1); //test1
marty.printName.apply(test2); //test2
marty.printName(); //marty
test3.printName(); //test3
}
test();
var bar = {
a:'1'
};
function test(){
bar.a = 'a';
Object.prototype.b = 'b';
return function inner(){
console.log(bar.a);
console.log(bar.b);
}
}
test()(); // a b
作业:
1.
function Foo(){
getName = function(){
console.log(1);
}
return this;
}
Foo.getName = function(){
console.log(2);
}
Foo.prototype.getName = function(){
console.log(3);
}
var getName = function(){
console.log(4);
}
function getName(){
console.log(5);
}
//这些打印出什么?
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
分析:
Foo.getName();
getName();
Foo().getName(); //1
getName();
new Foo.getName(); // new 2 -->2
new Foo().getName(); // (new Foo()).getName() --> 到this里头没有,到原型里头去找,有找到 3
new new Foo().getName(); // new 3 -->3
- 点运算 比 new 的优先级高
- 括号的优先级大于点
- new Foo( ) 有括号有new的时候,他们是一体的
实例化过后再调用方法
怎么样才能访问到原型上面
构造函数实例化了以后才有可能访问到原型上面
只要后边的有结果的前面怎么new 都没有用
运算符优先级表格
2. 请用 window.prompt接收用户输入的年份,判断是否是闰年?(用三元运算来做)
判断闰年的条件:
① 闰年能被4整除,但是不能被100整除
② 能被400整除两个条件二选一就可以实现判断,当然也可以将两个条件都写进去
var year = parseInt(window.prompt('请输入年份:'));
function isLeapYear(year){
var runYear = (year % 4 === 0 && year %100 !==0) || (year % 400 === 0) ? '是闰年' : '不是闰年';
console.log(runYear);
}
isLeapYear(year);