1.类型转换
1.1转换成String类型:
五大基础数据类型除了undefined和null,其余都可以使用toString()方法转换成String类型;
String()方法可以将五大基础数据类型转换成string类型,还可以使用+“”的方法;
1.2转换成Number类型:
Number()把字符串转换成数值类型的时候,如果字符串中有一个字符不是数字,返回NaN;
parseInt()无法把Boolean类型的数据转换成数值类型,返回NaN;
parseInt()在转换字符串的时候,会返回遇到第一个非数字字符之前的字符,如果此字符串以非数字字符开头,则返回NaN;
parseFloat()无法转换Boolean类型的数据,返回NaN;
parseFloat()会解析第一个. 直到遇到第二个. 或者非数字字符
取正 +XXX 可以将纯数字字符串转换成正整数,非纯数字子字符串会返回NaN,+true操作会返回1,+false操作返回0
取负 -XXX 可以将纯数字字符串转换成负整数,非纯数字字符串会返回NaN
+操作符如果有一边是字符串 一边是数字 那么会进行字符串的拼接
1.3转换成Boolean类型:
0、空字符串、null、undefined、 NaN都会转换成false,其他都会转换成true
!!XXX会将XXX转换成Boolean类型
2.预解析
javascript解析器执行javascript代码的时候分为两个过程:预解析过程和代码执行过程
预解析过程中:
2.1 var声明的变量和函数声明会被提升到作用域的最前面,如果var声明的变量和函数同名,那么函数优先,也就是函数声明的提升会覆盖var 声明的变量
2.2 有如下代码 var a = b = c = d = 5; 那么变量提升的只有a, 剩下的b、c、d三个变量都是全局变量的声明,没有提升,随处可用
3.new做了什么:
3.1在内存中创建了一个对象
3.2让构造函数中的this指向了刚刚创建的对象
3.3执行构造函数,在构造函数中设置属性和方法,当然还可以有别的操作
3.4返回当前创建的对象
4.基本包装类型:
var bool = new Boolean(false);
console.log(bool && true);//打印true 这是因为变量bool是个对象实例,他的原始值是传入的false,但是进行boolean类型的转换的时候,只有0,空字符"",null、undefined,NaN这五种才会转换成false 其余都是true
5.原型:
5.1 实例的__proto__恒等于构造函数的prototype对象 s1.__proto__ === Student.prototype
5.2 实例的constructor记录了创建对象的构造函数 s1.constructor === Student
5.3 构造函数有一个属性prototype指向该构造函数的显示原型对象,
该显示原型对象又有一个属性constructor属性,指向该构造函数,
通过构造函数创建的实例对象有一个隐式原型对象__proto__ ,指向该构造函数的显示原型对象prototype
5.4 读取属性或方法的时候,先从实例自身查找,找不到的话就向原型链的上一级查找,直到找到或者查找到了原型链的顶端,找不到就会返回undefined
但是,设置属性的时候是不会去查找原型链的,如果自身有这个属性就设置,没有的话就新增该属性,并且设置的属性会覆盖掉原型链上的同名属性。
5.5 如果重置了构造函数的显示原型对象prototype,记得修正这个显示原型对象的constructor指向
function Student(name, age){ 2 this.name = name; 3 this.age = age; 4 } 5 6 Student.prototype = { 7 8 constructor:Student, //这里是必须的,因为这种写法相当于重新修改了Student.prototype对象,将其指向了一个新的Object对象,所以要手动将显示原型对象的constructor属性重新指向原来的构造函数Student 9 10 sayHi:function(){ 11 console.log("sayhi"); 12 } , 13 eat:function(){ 14 console.log("eat"); 15 } 16 } var stu = new Student("xiaoming",19); stu.sayHi();//实例要想读取原型上的属性或方法,那么必须在原型上设置好之后,才能实例化再进行读取,否则在实例化之后再去原型上设置的话是读取不到的,会报错
5.6 数组和String类型的显示原型对象是不允许直接覆盖修改的,只能通过Array.prototype.xxx = xxxx 这种方式修改,否则即便进行了覆盖修改,数组和String的显示原型对象上原来那些内置还是会存在,覆盖修改里面新增的方法是不会存在的,也不会起作用的
6.自调用函数:
6.1自调用函数可以创建一个私有的作用域,避免污染全局变量
6.2书写自调用函数的时候 一定一定一定在最前面加上一个; 避免这个自调用函数跟前面的语句发生结合,导致报错
6.3使用自调用函数的时候,实参一般会加上window和undefined
添加window实参是为了可以压缩代码
添加undefined是因为低版本的浏览器中undefined值可以被修改,所以为了防止出错,在自调用函数中的是惨重传入undefined
1 ;(function(window, undefined){ 2 //window.Game = Game; //window.xxx.......... 3 })(window, undefined)//传入实参window和undefined
7.继承
7.1原型继承:设置继承关系的时候只能实例化父类一次,所以对于子类来说就相当于不能设置构造函数的参数
1 // 父类型 2 function Person() { 3 this.name = 'zs'; 4 this.age = 18; 5 this.sex = '男'; 6 } 7 8 // 子类型 9 function Student() { 10 this.score = 100; 11 } 12 13 Student.prototype = new Person();//继承这里只能实例化一次,不能设置构造函数的参数 14 Student.prototype.constructor = Student; 15 16 17 var s1 = new Student(); 18 console.log(s1.constructor); 19 console.dir(s1); 20 21 function Teacher() { 22 this.salary = 3000; 23 }
7.2构造函数继承:利用call方法,改变父类的this指向,相当于把子类当成参数传入到父类并指向父类的this,然后执行一遍父类的代码,相当于设置一遍子类没有的属性,相当于复制一遍父类的属性,缺点是不能继承父类原型上的方法
1 // 父类型 2 function Person(name, age, sex) { 3 this.name = name; 4 this.age = age; 5 this.sex = sex; 6 // this.sayHi //借用构造函数继承,可以继承父类的属性和方法,如果在这里面加上方法,那么每个子类的实例都会为这个方法申请一个内存,造成浪费 7 } 8 Person.prototype.sayHi = function () { 9 console.log(this.name);//借用构造函数继承,不能继承父类原型上的方法 10 } 11 12 // 子类型 13 function Student(name, age, sex, score) { 14 Person.call(this, name, age, sex); 15 this.score = score; 16 } 17 18 var s1 = new Student('zs', 18, '男', 100); 19 console.dir(s1);
7.3组合继承:利用原型继承来继承父类原型上的方法,再利用构造函数继承来继承父类的属性。由此,多个子类型也可以在自己的子类的原型上添加自己的独有的方法,不会影响别的子类,比如学生有自己的exam方法,而老师类就不会有
1 //父类型 2 function Person(name, age, sex) { 3 this.name = name; 4 this.age = age; 5 this.sex = sex; 6 } 7 8 Person.prototype.sayHi = function () { 9 console.log('大家好,我是' + this.name); 10 } 11 12 // 子类型1:Student 13 function Student(name, age, sex, score) { 14 // 借用构造函数 15 Person.call(this, name, age, sex); 16 17 this.score = score; 18 } 19 20 // 通过原型,让子类型,继承父类型中的方法 21 Student.prototype = new Person(); 22 Student.prototype.constructor = Student; 23 // 学生特有的方法 24 Student.prototype.exam = function () { 25 console.log('考试'); 26 } 27 28 //子类型2:Teacher 29 function Teacher(name, age, sex, salary) { 30 // 借用构造函数 31 Person.call(this, name, age, sex); 32 33 this.salary = salary; 34 } 35 36 // 通过原型让子类型继承父类型中的方法 37 Teacher.prototype = new Person(); 38 Teacher.prototype.constructor = Teacher; 39 40 var t1 = new Teacher('ww', 30, '男', 100000); 41 console.dir(t1); 42 43 t1.sayHi();
附上一张继承的原型图
8.this指向: 函数内部的this不是由书写时候确定的, 而是由调用的时候确定的
//1 普通函数调用 this指向window function fn() { console.log(this); } window.fn(); //2 方法调用 this指向调用该方法的对象 var obj = { fn: function () { console.log(this); } } obj.fn(); //3 作为构造函数调用 构造函数内部的this指向由该构造函数创建的对象 //4 作为事件的处理函数 this指向触发该事件的对象 btn.onclick = function() { console.log(this); } //5 作为定时器的参数 this指向window setInterval(function () { console.log(this); }, 1000); //终结总结:函数内部的this,是由函数调用的时候来确定其指向的 //栗子1: function fn() { console.log(this); } fn(); // this -> window //栗子2: function fn() { console.log(this); } var obj = { name: 'zs', fn: fn }: obj.fn();//this指向obj //栗子3: var obj = { name: 'zs', fn: function () { console.log(this); } }: var fn = obj.fn; fn(); // this -> window 重点哦 obj.fn();//this->obj
9.闭包:
之前针对闭包总结了一个帖子 https://www.cnblogs.com/buerjiongjiong/p/10875961.html
10.拷贝
10.1浅拷贝:简单类型直接拷贝,复杂类型拷贝的是复杂对象的内存地址,拷贝后都指向同一个内存地址,如果有一个地方修改了,那么将会影响所有指向该内存地址的对象。浅拷贝表面上来理解就是只是拷贝了一层
10.2深拷贝:相对于浅拷贝来说就是复制了多层,简单数据类型直接拷贝,遇到复杂类型就会遍历这个复杂类型的所有属性,简单数据直接拷贝,复杂数据类型再进行遍历,直至没有复杂数据类型,这样就不会拷贝到复杂类型的内存地址,拷贝之后的结果即便修改了也互不影响。
1 function deepCopy(sourceObj,rltObj){ 2 for(var key in sourceObj){ 3 if(sourceObj[key] instanceof Object){ 4 //复杂数据类型递归拷贝 5 rltObj[key] = {}; 6 deepCopy(sourceObj[key],rltObj[key]); 7 }else if(sourceObj[key] instanceof Array){ 8 //复杂数据类型递归拷贝 9 rltObj[key] = []; 10 deepCopy(sourceObj[key],newArr); 11 }else{ 12 rltObj[key] = sourceObj[key]; 13 } 14 } 15 }
11.正则表达式:
12.数组去重:参考可单列一张博客随笔
13. let
- let声明的变量不存在预解析,必须先声明再使用(暂时性锁区:使用let声明的变量在声明之前的区域是不能够使用这个变量的)
- let声明的变量不允许重复声明(在同一个作用域内)
- ES6引入了块级作用域,在块内使用let声明的变量在块外面是不能够使用的
1 console.log(a);//undefined 2 console.log(b);//Uncaught ReferenceError: b is not defined 3 { 4 console.log(a);//undefined 5 console.log(b);//报错了,因为暂时性锁区 Uncaught ReferenceError: Cannot access 'b' before initialization 6 var a = 0; 7 let b=1; 8 console.log(a);//0 9 console.log(b);//1 10 } 11 console.log(a);//0 12 console.log(b);//Uncaught ReferenceError: b is not defined
14.const:用来声明常量
- const声明的常量不允许重新赋值
- const声明的常量必须初始化
- 适应于let声明的规则也适应于const,也就是上面第13条关于let的总结的规则const也要遵守
15.箭头函数:
- 箭头函数中的this取决于定义时候,而不是调用的时候
- 箭头函数不可以new
- 箭头函数不可以使用arguents获取参数,可以使用剩余参数rest代替
let foo = (...param)=>{ console.log(param); } foo(1,'2',3);//输出[1,'2',3]
16.变量的解构赋值:
- 数组的解构赋值:按照数组下标的顺序依次赋值,还可以设置默认值
1 let [a, b, c] = [1, 2, 3]; 2 console.log(a, b, c);//1,2,3 3 4 let [d, e, f] = [, 5,]; 5 console.log(d, e, f);//undefined,5,undefined 6 7 let [i = 7, j, k] = [, 8, 9] 8 console.log(i, j, k);//7,8,9
- 对象的解构赋值:根据键的名称赋值
1 let { foo, bar } = { bar: 'hi', foo: 'hello' }; 2 console.log(foo, bar);//helo hi 3 4 //属性重命名,原来的变量名称就不能用了 5 let { foo: newFoo, bar } = { bar: 'hi', foo: 'hello' }; 6 console.log(newFoo,bar);//hello hi 7 console.log(foo)//报错Uncaught ReferenceError: foo is not defined,因为解构赋值的时候foo已经被重命名为newFoo了 8 9 //默认值 10 let {foo = 'hello0000',bar} = {bar:'hi'}; 11 console.log(foo,bar)//hello0000 hi
- 字符串的解构赋值:基本上跟数组的解构赋值差不多,只是解构赋值字符串的length属性的时候要是用对象解构赋值的方式,而不是数组
1 let [a,b,c] = 'hello'; 2 console.log(a,b,c);//h e l 3 4 let [a,b,c,d,e,f] = 'hello'; 5 console.log(a,b,c,d,e,f);//h e l l o undefined 6 7 let {length} = 'hello';//解构赋值字符串的length属性的时候要是用对象结构的方式,而不是数组 8 console.log(length);//5
- 函数参数的解构赋值
1 function foo({ name, age, sex = '男' } = {}) {//除了sex的默认值,foo函数整体也给了一个默认值{} 2 console.log(name, age, sex);//张三 18 男 3 } 4 5 foo({ name: '张三', age: 18 });
17.剩余参数rest和扩展运算符
- 剩余参数rest:剩余参数以数组方式读取
1 function foo(a,b,...params){ 2 console.log(a,b,params);//1 2 [3,4,5] 3 } 4 5 foo(1,2,3,4,5)
- 扩展运算符
1 function foo(a, b, c) { 2 console.log(a, b, c);//1 2 3 3 } 4 5 foo(...[1, 2, 3]); 6 7 8 //类似于函数的apply方法 9 function foo(a, b, c) { 10 console.log(a, b, c);//1 2 3 11 } 12 13 foo.apply(null, [1, 2, 3]);
1 //数组合并 2 let arr = [1,2,3]; 3 let arr2 = [3,4,3]; 4 let arr3 = [...arr,...arr2] 5 console.log(arr3);// [1, 2, 3, 3, 4, 3]
18.