JS高级篇一:对象、构造函数、继承

  2 ## JS基本介绍
  3 + JS的用途:Javascript可以实现浏览器端、服务器端(nodejs)。。。
  4 + 浏览器端JS由以下三个部分组成:
  5     - ECMAScript:基础语法(数据类型、运算符、函数。。。)
  6     - BOM(浏览器对象模型):window、location、history、navigator。。。
  7     - DOM(文档对象模型):div、p、span。。。
  8 + ECMAScript又名es,有以下重大版本:
  9     - 旧时代:
 10         - es1.0。。。es3.1
 11     - 新时代:
 12         - es5
 13         - es6(es2015)
 14         - es7(es2016)、es8(es2017)
 15 
 16 ## 数据类型
 17 + 基本数据类型——值类型:(数字、字符串、布尔值、null、undefined)
 18     - undefined类型?
 19 + 复杂数据类型——引用类型:(对象)
 20     - 数组
 21     - 函数
 22     - 正则表达式
 23     - Date
 24 
 25 ## 对象的基本使用
 26 ### 创建一个对象
 27 ```js
 28     var student={ 
 29         name:"李白" , //student有一个name属性,值为"李白"
 30         grade:"初一" ,
 31         //a、student有一个say属性,值为一个函数
 32         //b、student有一个say方法
 33         say:function(){
 34             console.log("你好");
 35         },
 36         run:function(speed){
 37             console.log("正在以"+speed+"米/秒的速度奔跑");
 38         }
 39     }
 40 ```
 41 
 42 ### 对象是键值对的集合:对象是由属性和方法构成的 (ps:也有说法为:对象里面皆属性,认为方法也是一个属性)
 43 + name是属性    grade是属性
 44 + say是方法     run是方法
 45 
 46 ### 对象属性操作
 47 #### 获取属性:
 48 #### 第一种方式:.语法
 49 + student.name      获取到name属性的值,为:"李白"
 50 + student.say       获取到一个函数
 51 
 52 #### 第二种方式:[]语法
 53 + student["name"]   等价于student.name
 54 + student["say"]    等价于student.say
 55 
 56 #### 号外:2种方式的差异:
 57 + .语法更方便,但是坑比较多(有局限性),比如:
 58     - .后面不能使用js中的关键字、保留字(class、thisfunction。。。)
 59     - .后面不能使用数字
 60 ```js
 61     var obj={};
 62     obj.this=5; //语法错误
 63     obj.0=10;   //语法错误
 64 ```
 65 
 66 + []使用更广泛
 67     - o1[变量name]
 68     - ["class"]、["this"]都可以随意使用 `obj["this"]=10`
 69     - [0]、[1]、[2]也可以使用       
 70         - `obj[3]=50 = obj["3"]=50`     
 71         - 思考:为什么obj[3]=obj["3"]
 72     - 甚至还可以这样用:["[object Array]"]
 73         - jquery里面就有这样的实现
 74     - 也可以这样用:["{abc}"]
 75         - 给对象添加了{abc}属性
 76 
 77 #### 设置属性
 78 + `student["gender"]="男"`    等价于:    `student.gender="男"`
 79     - 含义:如果student对象中没有gender属性,就添加一个gender属性,值为"男"
 80     -      如果student对象中有gender属性,就修改gender属性的值为"男"
 81 + 案例1:`student.isFemale=true`
 82 + 案例2:`student["children"]=[1,2,5]`
 83 + 案例3:
 84 ```js
 85     student.toShanghai=function(){   
 86         console.log("正在去往上海的路上")   
 87     }
 88 ```
 89 
 90 #### 删除属性
 91 + delete student["gender"]      
 92 + delete student.gender
 93 
 94 
 95 
 96 ## 通过构造函数创建对象
 97 ### 构造函数创建对象的例子:
 98 + var xiaoming = new Object()     -->   var xiaoming = {};  
 99 + var now = new Date() 
100 + var rooms = new Array(1,3,5)    -->   var rooms = [1,3,5]
101 + `var isMale=/123/;`   ==> `var isMale=new RegExp("123")`
102     - isMale是通过RegExp构造函数创建出来的对象
103     - isMale是RegExp构造函数的实例
104 
105 + 以上例子中,Object、Date、Array都是内置的构造函数
106 
107 ## 自定义一个构造函数来创建对象
108 + 构造函数
109 ```js
110     function Person(name,age){
111         this.name=name;
112         this.age=age;
113     }
114     var p1=new Person("赵云",18)
115 ```
116 + 说明:`p1就是根据【Person构造函数】创建出来的对象`
117 
118 ### 构造函数的概念
119 + 任何函数都可以当成构造函数
120     `function CreateFunc(){ }`
121 + 只要把一个函数通过new的方式来进行调用,我们就把这一次函数的调用方式称之为:构造函数的调用
122     - new CreateFunc(); 此时CreateFunc就是一个构造函数
123     - CreateFunc();     此时的CreateFunc并不是构造函数
124 
125 ### 关于new Object()
126 + new Object()等同于对象字面量{}
127 
128 ### 构造函数的执行过程
129 `var p1=new Person();`
130 + 1、创建一个对象 (我们把这个对象称之为Person构造函数的实例)- `_p1 `
131 + 2、创建一个内部对象,`this`,将this指向该实例(_p1)
132 + 3、执行函数内部的代码,其中,操作this的部分就是操作了该实例(_p1)
133 + 4、返回值:
134     - a、如果函数没有返回值(没有return语句),那么就会返回构造函数的实例(p1)
135     - b、如果函数返回了一个基本数据类型的值,那么本次构造函数的返回值是该实例(_p1)
136     ```js
137         function fn(){
138             
139         }
140         var f1=new fn();    //f1就是fn的实例
141 
142         function fn2(){
143             return "abc";
144         }
145         var f2=new fn2();   //f2是fn2构造函数的实例
146     ```
147     - c、如果函数返回了一个复杂数据类型的值,那么本次函数的返回值就是该值
148     ```js
149         function fn3(){
150             return [1,3,5]; 
151             //数组是一个对象类型的值,
152             //所以数组是一个复杂数据类型的值
153             //-->本次构造函数的真正返回值就是该数组
154             //-->不再是fn3构造函数的实例
155         }
156         var f3=new fn3();   //f3还是fn3的实例吗?错
157         //f3值为[1,3,5]
158     ```
159 
160 
161 
162 ## 继承
163 ### JS中继承的概念:
164 + 通过【某种方式】让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承  `并不是所谓的xxx extends yyy`
165 
166 ### 为什么要使用继承?
167 + 有些对象会有方法(动作、行为),而这些方法都是函数,如果把这些方法和函数都放在构造函数中声明就会导致内存的浪费
168 ```js
169     function Person(){
170         this.say=function(){
171             console.log("你好")
172         }
173     }
174     var p1=new Person();
175     var p2=new Person();
176     console.log(p1.say === p2.say);   //false
177 ```
178 
179 ### 继承的第一种方式:原型链继承1
180 ```js
181     Person.prototype.say=function(){
182         console.log("你好")
183     }
184 ```
185 + 缺点:添加1、2个方法无所谓,但是如果方法很多会导致过多的代码冗余
186 
187 ### 继承的第二种方式:原型链继承2
188 ```js
189     Person.prototype = {
190         //切记不能忘记
191         constructor:Person,
192         say:function(){
193             console.log("你好");
194         },
195         run:function(){
196             console.log("正在进行百米冲刺");
197         }
198     }
199 ```
200 + 注意点:
201 + a、一般情况下,应该先改变原型对象,再创建对象
202 + b、一般情况下,对于新原型,会添加一个constructor属性,从而不破坏原有的原型对象的结构
203 
204 ### 继承的第三种方式:拷贝继承(混入继承:mixin)
205 + 场景:有时候想使用某个对象中的属性,但是又不能直接修改它,于是就可以创建一个该对象的拷贝
206 + 实际运用:
207     - jquery:$.extend:编写jquery插件的必经之路
208         - 基于jquery封装一个表格控件
209 
210 ```js
211     var o1={ age:2 };
212 
213     var o2 = o1;
214     o2.age=18;      
215     //1、修改了o2对象的age属性
216     //2、由于o2对象跟o1对象是同一个对象
217     //3、所以此时o1对象的age属性也被修改了
218 ```
219 ```js
220     var o3={gender:"男",grade:"初三",group:"第五组",name:"张三"};
221     var o4={gender:"男",grade:"初三",group:"第五组",name:"李四"};
222     //上述代码中,如果使用拷贝继承对代码进行优化会非常和谐
223 
224     //实现拷贝继承:
225     //1、已经拥有了o3对象
226     //2、创建一个o3对象的拷贝(克隆):for...in循环
227     
228 
229     //3、修改克隆对象,把该对象的name属性改为"李四"
230     
231 ```
232 
233 + 实现1:
234 ```js
235     var source={name:"李白",age:15}
236     var target={};
237     target.name=source.name
238     target.age=source.age;
239 ```
240 
241 + 浅拷贝和深拷贝
242     - 浅拷贝只是拷贝一层属性,没有内部对象
243     - 深拷贝其实是利用了递归的原理,将对象的若干层属性拷贝出来
244     ```js
245         var students=[
246             {name:"",age:""},
247             {name:"",age:""}
248         ]
249     ```
250 
251 + 上面的方式很明显无法重用,实际代码编写过程中,很多时候都会使用拷贝继承的方式,所以为了重用,可以编写一个函数把他们封装起来:
252 ```js
253     function extend(target,source){
254         for(key in source){
255             target[key]=source[key];
256         }
257         return target;
258     }
259     extend(target,source)
260 ```
261 
262 + 由于拷贝继承在实际开发中使用场景非常多,所以很多库都对此有了实现
263     - jquery:$.extend
264 
265 + es6中有了 <对象扩展运算符> 仿佛就是专门为了拷贝继承而生:
266     - 优点:简单的令人发指
267 ```js
268     var source={name:"李白",age:15}
269     //让target是一个新对象,同时拥有了name、age属性
270     var target={ ...source }
271     
272     var target2={ ...source,age:18 }
273 ```
274 
275 ### 继承的第四种方式:原型式继承:(道格拉斯在蝴蝶书中提出来的)
276 + 场景:
277     - a、创建一个纯洁的对象:对象什么属性都没有
278     - b、创建一个继承自某个父对象的子对象
279         ```js
280             var parent={ age:18,gender:"男"};
281             var student=Object.create(parent);
282             //student.__proto__===parent
283         ```
284 + 使用方式:
285     - 空对象:Object.create(null)
286     - 
287     ```js
288         var o1={ say:function(){} }
289         var o2=Object.create(o1);
290     ```
291 
292 ### 继承的第五种方式:借用构造函数实现继承
293 + 场景:适用于2种构造函数之间逻辑有相似的情况
294 + 原理:函数的call、apply调用方式
295 
296 ```js
297 function Animal(name,age,gender){
298     this.name=name;
299     this.age=age;
300     this.gender=gender;
301 }
302 function Person(name,age,gender,say){
303     this.name=name;
304     this.age=age;
305     this.gender=gender;
306 
307     this.say=function(){
308 
309     }
310 }
311 ```
312 + 局限性:Animal(父类构造函数)的代码必须完全适用于Person(子类构造函数)
313 
314 + 以上代码用借用构造函数实现
315 ```js
316 function Animal(name,age){
317     this.name=name;
318     this.age=age;
319 }
320 function Person(name,age,address){
321     Animal.call(this,name);
322     //this.name=name;
323     //this.age=age;
324     this.address=address;
325 }
326 ```
327 
328 + 寄生继承、寄生组合继承
329 
330 ## 原型链(家族族谱)
331 + 概念:JS里面的对象可能会有父对象,父对象还会有父对象,。。。。。祖先
332 + 根本:继承
333     - 属性:对象中几乎都会有一个__proto__属性,指向他的父对象
334         -意义:可以实现让该对象访问到父对象中相关属性
335 + 根对象:Object.prototype
336     - var arr=[1,3,5]
337     - arr.__proto__:Array.prototype
338     - arr.__proto__.__proto__找到了根对象
339 
340     ```js
341         function Animal(){}
342         var cat=new Animal();
343         //cat.__proto__:Animal.prototype
344         //cat.__proto__.__proto__:根对象
345     ```
346 + 错误的理解:万物继承自Object?
347 
348 ## 闭包
349 ### 变量作用域
350 + 变量作用域的概念:就是一个变量可以使用的范围
351 + JS中首先有一个最外层的作用域:称之为全局作用域
352 + JS中还可以通过函数创建出一个独立的作用域,其中函数可以嵌套,所以作用域也可以嵌套
353 ```js
354 var age=18;     //age是在全局作用域中声明的变量:全局变量
355 
356 function f1(){
357     console.log(name);      //可以访问到name变量
358     var name="周董" //name是f1函数内部声明的变量,所以name变量的作用域就是在f1函数内部
359 
360     console.log(name);      //可以访问到name变量
361 
362     console.log(age);       //age是全局作用域中声明的,所以age也可以访问
363 }
364 
365 console.log(age);       //也可以访问
366 ```
367 
368 ```js
369     //多级作用域
370     //-->1级作用域
371     var gender="男";
372     function fn(){
373         //问题:
374         //gender:可以访问
375         //age:  可以访问
376         //height:不能访问
377 
378         //-->2级作用域
379         return function(){
380             //问题:
381             //gender:   通过一级一级作用域的查找,发现gender是全局作用域中声明的变量
382             //age:
383             //height:
384             console.log(gender);
385 
386             //-->3级作用域
387             var height=180;
388         }
389         var age=5;
390     }
391 ```
392 
393 ### 作用域链
394 + 由于作用域是相对于变量而言的,而如果存在多级作用域,这个变量又来自于哪里?这个问题就需要好好地探究一下了,我们把这个变量的查找过程称之为变量的作用域链
395 + 作用域链的意义:查找变量(确定变量来自于哪里,变量是否可以访问)
396 + 简单来说,作用域链可以用以下几句话来概括:(或者说:确定一个变量来自于哪个作用域)
397     - 查看当前作用域,如果当前作用域声明了这个变量,就确定结果
398         - 查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明
399             - 再查找上级函数的上级函数,直到全局作用域为止
400                 - 如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)
401 
402 + 举例1:
403 ```js
404     var name="张三";
405     function f1(){
406         var name="abc";
407         console.log(name);
408     }
409     f1();
410 ```
411 
412 + 举例2:
413 ```js
414     var name="张三";
415     function f1(){
416         console.log(name);
417         var name="abc";
418     }
419     f1();
420 ```
421 
422 + 举例3:
423 ```js
424     var name="张三";
425     function f1(){
426         console.log(name);
427         var name="abc";
428     }
429     f1();
430 ```
431 
432 + 举例4:
433 ```js
434     var name="张三";
435     function f1(){
436         return function(){
437             console.log(name);
438         }
439         var name="abc";
440     }
441     var fn=f1();
442     fn();
443 ```
444 
445 + 举例5:
446 ```js
447     var name="张三";
448     function f1(){
449         return {
450             say:function(){
451                 console.log(name);
452                 var name="abc";
453             }
454         }
455     }
456     var fn=f1();
457 ```
458 
459 ### 闭包的问题
460 ```js
461     function fn(){
462         var a=5;
463         return function(){
464             a++;
465             console.log(a);
466         }
467     }
468     var f1=fn();
469     f1();
470     f1();
471     f1();
472 ```
473 
474 ### 闭包问题的产生原因
475 + 函数执行完毕后,作用域中保留了最新的a变量的值
476 
477 ### 闭包的应用场景
478 + 模块化
479 + 防止变量被破坏
480 
481 ### 函数的4种调用方式
482 + 1、函数调用
483 ```js
484     var age=18;
485     var p={
486         age:15
487         say:function(){
488             console.log(this.age);
489         }
490     }
491     var s1=p.say()
492     s1();       //函数调用
493 ```
494 + 2、方法调用
495 ```js
496     var age=18;
497     var p={
498         age:15
499         say:function(){
500             console.log(this.age);
501         }
502     }
503     p.say()//方法调用
504 ```
505 + 3、new调用(构造函数)
506 ```js
507     var age=18;
508     var p={
509         age:15
510         say:function(){
511             console.log(this.age);
512         }
513     }
514     new p.say()//构造函数调用
515 ```
516 + 4、上下文方式(call、apply、bind)
517 ```js
518     var length=21;
519     function f1(){
520         console.log(this.length);
521     }
522     f1.call([1,3,5])
523     f1.apply(this)
524     f1.call(5)
525 ```
526 
527 + 在ES6的箭头函数之前的时代,想要判断一个函数内部的this指向谁,就是根据上面的四种方式来决定的
528 
529 
530 
531 
532 
533 ### es6内容
534 + 1、解构赋值   
535 + 2、函数rest参数  
536 + 3、箭头函数  
537     - 箭头函数和普通函数有哪些不同?(4点)
538 + 4、对象的Object.assign  
539 + 5、promise 
540 + 6、generator 
541 + 7、async 
542 + 8、class 
543 + 9、module
544 
545 ### 原型
546 + 原型很多人开发用不到?
547     - 很多人都用es6/7/8开发,确实用的比较少
548     - 如果你用es5之前的版本开发代码(IE8、IE7。。。),可能天天都要写原型
549     - 理解了原型,才是理解了JS面向对象的核心
550 + 类继承其实本质上还是用原型继承来(包装)的

猜你喜欢

转载自www.cnblogs.com/qfshini/p/12262750.html
今日推荐