版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://cloud.tencent.com/developer/article/1347593
之前看过一遍js高程,有些基础还不牢固,单身狗周末又没地方去,开始重新撸一遍JS高程,写点笔记,防止以后忘了!
引用类型是按引用传递的,函数的参数全部按值传递,有点蒙逼,看代码
//基本类型值没问题
function add(num) {
num += 10;
return num;
}
var count =20 ;
console.log(add(count));//30
//看不出来
/*高程给的解释:在这个函数内部,obj和person引用的是同一个对象。
*换句话说,即使这个对象是按值传递的,obj也会按引用来方位同一个对象*/
function setName (obj) {
obj.name = 'kenny';
}
var person = new Object();
setName(person); // 'kenny'
//函数内部重新定义obj对象
function setName (obj) {
obj.name = 'kenny';
obj = new Object();
obj.name = 'gaga';
}
var person = new Object();
setName(person); // 'kenny'
高程:函数内部重写obj时,这个变量引用的就是一个局部对象,而局部对象会在函数执行完毕后立即被销毁。个人理解就是函数作用域,外部环境无法访问内部的私有变量 ,包括arguments,其实相当于参数被重新定义成对象,但是外部的对象未变化,所以是按值传递,即对象的深度复制。
1.每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中, 虽然无法访问此对象,但js解析器会在后台使用它,(chorme 控制台js断点的时候,当前作用定义的变量和函数都能在当前执行环境查看,soga)。
2.每个函数都有自己的执行环境 ,当执行流进入一个函数时,函数的环境就会给推入一个环境栈中(后进先出)。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。每个环境多都可以向上搜索作用域链,以查询变量和函数名,(访问局部变量比访问全局变量要快,javascript引擎在优化标识符查村做的很ok,可以忽略不计)。
标记清除(大部分浏览器以此来实现)和引用计数
一旦数据不再引用,最好通过将其设置为null来释放引用——俗称解除引用。适用于大多数全局变量和全局对象的属性,局部变量会在他们离开执行环境时自动解除引用。
//执行完毕,person函数内的对象会自动销毁,
function person (name) {
var localPerson = new Object();
localPerson.name = name;
return localPerson;
}
var gloablPerson = person('kenny');
gloablPerson = null;
解除值的引用不会马上自动回收该值所占用的内存,是让值脱离执行环境,以便垃圾收集器下次运行将其回收。
对象字面量
通过对象字面量定义对象时,实际上不会调用Object构造函数(Firefox2及更早版本会),即不会调用new实例化对象。【ES3之前的正则字面量存在bug,循环test某个字符串,一次为真一次为假,ES5已修正(P105页)】
var isArray = value instanceof Array;
以上代码要返回true就,value必须是数组,而且还必须与Array构造函数在同个全局作用域中,(切记,Array是window的属性),如果value在另个frame定义的数组,那么以上代码就会返回false。
在任何值上调用Object原声的toString()方法,都会返回一个object NativeConstructorName格式的字符串,由于原生数组的构造函数与全局作用域无关,因此toString()就能保证返回一致的值。
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
function isFunction(value) {
return Object.prototype.toString.call(value) === '[object Function]';
}
function isRegExp(value) {
return Object.prototype.toString.call(value) === '[object RegExp]';
}
在Web开发中能够区分原生与非原生javascript对象非常重要,这样才能确切知道某个对象到底有哪些功能,但是Object.prototype.toString()本身也能被重写。
每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
//函数是对象,函数名是指针
function sum(num1, num2) {
return num1 + num2;
}
console.log(sum(10,10)); //20
var anotherSum = sum; //使用不带圆括号的函数名是访问函数指针,而非调用函数
console.log(anotherSum(10,10)); //20
sum = null; //设置其他值也一样
console.log(anotherSum(10, 10))//20
so要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号。
js解析器会率先读取函数声明(function declaration hosting),并使其在执行任何代码之前可用,至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。
function createComparisonFunction(propertyName) {
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}
var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}];
data.sort(createComparisonFunction("name"));
alert(data[0].name); //Nicholas
data.sort(createComparisonFunction("age"));
alert(data[0].name); //Zachary
函数内部有两个特殊对象:arguments和this,arguments.callee指向拥有arguments对象的函数(ES6已弃用)。
this引用的是函数据以执行的环境对象,即函数调用,蝴蝶书上讲过有四种调用模式
1.方法调用模式,this指向当前方法的对象;
2.函数调用模式,this被绑定到全局对象,客户端即window;
3.构造器调用模式,this被绑定到实例化的新对象上;
4.apply/call调用模式,第一个参数都是要被绑定给this,第二个参数apply是数组,call([thisObj[,arg1[, arg2[, ,.argN]]]])。
每个函数都包含两个属性: length(函数希望接收的命名参数的个数) 和 prototype。
对于ECMAScript中的引用类型而言,prototype是保存她们所有实例方法的真正所在。在ES5中,prototype属性是不可枚举的,(非原生的prototype属性可枚举,即人为添加上去的,在google测试过)因此使用for-in无法发现。
ES5还定义了一个方法: bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。
var s1 = 'some text';
var s2 = s1.substring(2);
上述可以解析成三个步骤
1.创建String类型的一个实例;
2.在实例上调用指定的方法;
3.销毁这个实例
执行代码类似如下:
var s1 = new String('some text');
var s2 = s1.substring(2);
s1 = null;
引用类型与基本包装类型的主要区别就是对象的生存期。使用new操作符创建的引用类型实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型,则只存在于一行代码的执行瞬间,然后理解被销毁。
在所有代码执行之前,作用域中就已经存在两个内置对象:Global 和Math。在大多数ECMAScript视线中都不能直接访问Global对象;不过WEB浏览器实现了承担该角色的window对象。全局变量和函数都是Global对象的属性。Math对象提供了很多属性和方法,用于辅助完成复杂的数学计算。