1.数值,布尔值,对象和字符串值都有toString()方法
2.一元加操作符放在数值面前,对数值不会产生任何影响,但应用非数值时会先将该值转换
var a = 'a';
a = +a; // NaN
3.一元减操作符应用数值时,该值会变成负数
1.var a = 4; var b = a; a = 5; console.log(b); // b = 4
a是基本类型,var b = a表示创建了b,将a的值复制到b,两者独立,修改一个值不会影响另外一个的值,基本类型的值被保存在栈内存中
2.var a = {}; var b = a; a.name = ‘a’; console.log(b.name); // b.name = ‘a’;
a是引用类型,b复制了a,两者指向同一对象,引用类型的值被保存在堆内存中
3.原始引用不变,函数内部重写了obj,该变量引用的是一个局部对象,函数执行完后会销毁该局部对象
function setName(obj) {
obj.name = 'a';
obj = new Object();// 局部对象
obj.name = 'b';
}
var p = new Object()
setName(p);
console.log(p.name) // a
4.检测类型的方法:typeof, instanceof
5.作用域:代码的执行环境,粗略可理解花括号封闭的代码块都有自己的作用域,没有用var定义声明的变量会被添加到全局环境中,所有变量都存在于一个执行环境(也称为作用域),每次进入一个新执行环境都会创建一个用于搜索变量和函数的作用域链。变量的执行环境有助于确定应该何时释放内存。
作用域里面装的是执行期上下文AO和GO的集合,每一次函数调用都会生成一个新的AO对象,函数对象有一个【scope】属性,里面存的是作用域链。在函数中访问一个变量时,先找AO对象里的变量,没有时再往下找GO,当函数执行完毕后,局部活动对象(AO)就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。
作用域本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
1.数组的length属性不是只读的,通过修改该属性可以间接修改数组
2.sort方法接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数应该位于第二个之后则返回一个正数(两个值需要调位置返回正数)
3.所有在全局作用域中定义的属性和函数,都是Global对象的属性。Global对象有encodeURI和encodeRRIComponent方法,(URI – Uniform Resource Identifiers 通用资源标识符)。
encodeURI将空格替换成%20
encodeRRIComponent替换所有非字母数字字符
4.eval能够解析代码字符串
5.Math对象方法:max, min var max = Math.max(3,54,5)
// 找到数组中的最大值 Math.max.apply(Math, [1,2,3])
// 向上取整 Math.ceil
// 向下取整 Math.floor
// 四舍五入 Math.round
// 随机数 Math.random 返回0~1
6.判断类型:Object.prototype.toString.call(x)
1.访问器属性不包含数据值;它们包含一对getter和setter函数(两函数非必需),在读取访问器属性时会调用getter函数,在写入访问器属性时会调用setter函数并传入新值。
2.构造函数,实例,原型对象三者关系
构造函数默认有个prototype属性,该属性指向函数的原型对象。
原型对象默认有个constructor属性指向构造函数。
构造函数创建的新实例有个内部属性__proto__
,不可见,该连接存在于实例和原型对象间,不存在实例和构造函数。
hasOwnProperty()检测一个属性是存在于实例中还是原型中(只存在实例中才会返回true)
in操作符检测对象是否能访问到某个属性(包含实例和原型)
// 判断属性是存在于实例中还是对象中
function hasPrototypeProperty(object,name) {
return !object.hasOwnProperty(name) && (name in object);
}
另:通过对象实例可以访问保存在原型中的值,但不可通过实例重写原型中的值。当实例增加一个属性时,会屏蔽掉原型对象中保存的同名属性(原型对象中的属性会被阻止访问,并非被修改)
3.采用对象字面量重写原型对象,本质上是完全重写了默认的prototype对象,因此constructor属性变成了新对象的constructor属性(指向object构造函数),可以通过重设该属性访问到正确的值,但是constructor会变成可枚举
function Person(){};
Person.prototype = {
constructor: Person,
name: 'a',
age: 18,
}
4.构造函数模式和原型模式
// 原型对象中包含引用类型值的属性
function Person() {};
Person.prototype = {
constructor: Person,
friends: ['Amay', 'Bobby'], // friends属性在原型对象中
};
var person1 = new Person();// person1.friends和person2.friends指向同一个数组
var person2 = new Person();
person1.friends.push('Cindy');
console.log(person1.friends); // 'Amay,Bobby',Cindy'
console.log(person2.friends); // 'Amay,Bobby',Cindy'
组合模式:构造函数模式(定义实例属性)+原型模式(定义方法和共享的属性)
function Person(name) {
this.name = name;
this.friends = ['Amay', 'Bobby'];
};
Person.prototype = {
constructor: Person,
};
var person1 = new Person('a');// person1.friends和person2.friends引用了不同的数组
var person2 = new Person('b');
person1.friends.push('Cindy');
console.log(person1.friends); // 'Amay,Bobby',Cindy'
console.log(person2.friends); // 'Amay,Bobby'
总结:引用类型值不要写在原型里(原型属性会被所有实例共享),写在构造函数里。构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。
5.继承–依靠原型链实现:另一个原型是另一个类型的实例
1)访问实例属性的过程:先在实例中搜索该属性,如果没找到该属性,则会继续搜索实例的原型,在通过原型链实现继承的情况下,搜索过程沿着原型链继续向上搜索。
2)所有函数的默认原型都是Object的实例,所有自定义类型都会继承Object的默认方法(toString)
3)原型和实例的关系:isPrototypeOf()–只要是原型链中出现过的原型,该方法都会返回true
4)继承模式:组合继承,原型式继承,寄生式继承,寄生组合式继承
1.函数声明提升:在执行代码之前会先读取函数声明
2.闭包:有权访问另一个函数作用域中的变量的函数,由于闭包会携带包含它的函数的作用域(闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域),因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多。
3.立即执行函数(Immediately Invoked Functions Expressions,IIFE):用函数表达式的方式建立函数后并立即执行它(更多)。
(function(){
...
})();
因为我们只会在括号内放入表达式,例如(3+2),而不会放命令在括号内,所以JavaScript就会以表达式的方式来读取这段函数。在这种情况下,这个函数会被建立,但是不会被存在任何变量当中,也不会被执行。通过这样的方式,我们可以「直接执行某个函数」,一个很重要的一点是,这样做不仅避免了外界访问此 IIFE 内的变量,而且又不会污染全局作用域。
function(){
// 这里是块级作用域
}(); // 出错
上面代码导致语法报错,是因为在javascript会将function关键字当成一个函数声明的开始,而函数声明后面不能跟圆括号,只有函数表达式的后面可以跟圆括号(执行符合)。
立即执行函数可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链。