特性 函数 对象(pro js 2)
对象是JavaScript的重要单元。事实上JavaScript的一切都是对象或者是面向对象的接口。
语言特性
引用和值
JavaScript变量持有的数据有两种形式:拷贝和引用。任何的基本类型值都是拷贝到变量。基本类型值最重要的特性就是它们是以值的形式赋值、拷贝、传递以及从函数总返回。
JavaScript非基本类型值则是依赖引用。 任何不持有基本类型值的变量持有的是对象的引用。引用是指向对象内存位置的指针。这样有效率的是两个或者多个变量不会都来持有对象的拷贝;它们只是简单的指向这个对象就好。一个引用对对象的更新直接影响到另一个引用。
2 // (Using {} is shorter than 'new Object()')
3 var obj = {};
4 // objRef now refers to the other object
5 var refToObj = obj;
6 // Modify a property in the original object
7 obj.oneProperty = true;
8 // We now see that the change is represented in both variables
9 // (Since they both refer to the same object)
10 console.log( obj.oneProperty === refToObj.oneProperty );//true
11 // This change goes both ways, since obj and refToObj are both references
12 refToObj.anotherProperty = 1;
13 console.log( obj.anotherProperty === refToObj.anotherProperty );//true
自我调整的对象
2 // (Similar to 2-1, using [] is shorter than 'new Array()')
3 var items = [ 'one', 'two', 'three' ];
4 // Create a reference to the array of items
5 var itemsRef = items;
6 // Add an item to the original array
7 items.push( 'four' );
8 // The length of each array should be the same,
9 // since they both point to the same array object 10 console.log( items.length == itemsRef.length ); //true
重要的是要记住引用只指向所指对象,而不是另一个引用。
我们看一个实体对象改变,而引用继续指向旧式对象的例子:
2 var items = [ 'one', 'two', 'three' ];
3 // Set itemsRef to a reference to items
4 var itemsRef = items;
5 // Set items to equal a new object
6 items = [ 'new', 'array' ];
7 // items and itemsRef now point to different objects.
8 // items points to [ 'new', 'array' ]
9 // itemsRef points to [ 'one', 'two', 'three' ]
10 console.log( items !== itemsRef );
字符串是较有迷惑性的
2 var itemRef = item;
3 item += 'ing';//这里创建新的对象,但是基本类型
4 console.log(item, itemRef, item != itemRef );
5
6 test result:
7 //testing
8 //String {0: "t", 1: "e", 2: "s", 3: "t", length: 4, [[PrimitiveValue]]: "test"} 9 //false
2 var item = 'test';
3 // itemRef now refers to the same string object
4 var itemRef = item;
5 // Concatenate some new text onto the string object
6 // NOTE: This creates a new object and does not modify
7 // the original object.
8 item += 'ing';
9 // The values of item and itemRef are NOT equal, as a whole
10 // new string object has been created
11 console.log( item != itemRef );
作用域
JavaScript只有两个作用域:函数作用域和全局作用域。但是Es6里引入了块作用域。
JavaScript里变量作用域的运行原理
2 var foo = 'test';
3 // Within an if block
4 if ( true ) {
5 // Set foo equal to 'new test'
6 // NOTE: This still belongs to the global scope!
7 var foo = 'new test';
8 }
9 // As we can see here, as foo is now equal to 'new test'
10 console.log( foo === 'new test' );
11 // Create a function that will modify the variable foo
12 function test() {
13 var foo = 'old test';
14 }
15 // However, when called, 'foo' remains within the scope
16 // of the function
17 test();
18 // Which is confirmed, as foo is still equal to 'new test'
19 console.log( foo === 'new test' );
隐式的全局变量
2 function test() {
3 foo = 'test';
4 }
5 // Call the function to set the value of foo
6 test();
7 // We see that foo is now globally scoped 8 console.log( window.foo === 'test' );
Context上下文环境
通过this访问环境,它经常指向代码运行的环境。
2 this.foo = fooInput;
3 }
4 var foo = 5;
5 console.log( 'foo at the window level is set to: ' + foo );
6 var obj = {
7 foo : 10
8 };
9 console.log( 'foo inside of obj is set to: ' + obj.foo );
10 // This will change window-level foo
11 setFoo( 15 );
12 console.log( 'foo at the window level is now set to: ' + foo );
13 // This will change the foo inside the object
14 obj.setFoo = setFoo;
15 obj.setFoo( 20 );
16 console.log( 'foo inside of obj is now set to: ' + obj.foo );
这个函数setFoo有点奇怪。通常不会在普通的函数里使用this,这里是因为我们是要把setFoo关联到obj对象的。JavaScript有两种方法让你指定运行环境的:call、apply;
2 function changeColor( color ) {
3 this.style.color = color;
4 }
5 // Calling it on the window object, which fails, since it doesn't
6 // have a style object
7 changeColor('white' );
8 // Create a new div element, which will have a style object
9 var main = document.createElement('div');
10 // Set its color to black, using the call method
11 // The call method sets the context with the first argument
12 // and passes all the other arguments as arguments to the function
13 changeColor.call( main, 'black' );
14 //Check results using console.log
15 //The output should say 'black'
16 console.log(main.style.color);
17 // A function that sets the color on the body element
18 function setBodyColor() {
19 // The apply method sets the context to the body element
20 // with the first argument, and the second argument is an array
21 // of arguments that gets passed to the function
22 changeColor.apply( document.body, arguments );
23 }
24 // Set the background color of the body to black
25 setBodyColor('black' );
闭包
闭包是一种工具,我们可以通过它在内部函数中 在外部函数终止执行的时候访问外部函数出现的变量。
它提供给我们一种上下文环境,只能以某种形式访问。
1 // Find the element with an ID of 'main'
3 // Change its border styling
4 obj.style.border = '1px solid red';
5 // Initialize a callback that will occur in one second
6 setTimeout(function(){
7 // Which will hide the object
8 obj.style.display = 'none';
9 }, 1000);
10 // A generic function for displaying a delayed alert message
11 function delayedAlert( msg, time ) {
12 // Initialize an enclosed callback
13 setTimeout(function(){
14 // Which utilizes the msg passed in from the enclosing function
15 console.log( msg );
16 }, time );
17 }
18 // Call the delayedAlert function with two arguments
19 delayedAlert('Welcome!', 2000 );
这两种形式容易在发布代码到production过程中中压缩的时候易出现问题。
使用闭包实现科里化
2 function addGenerator( num ) {
3 // Return a simple function for adding two numbers
4 // with the first number borrowed from the generator
5 return function( toAdd ) {
6 return num + toAdd
7 };
8 }
9 // addFive now contains a function that takes one argument,
10 // adds five to it, and returns the resulting number
11 var addFive = addGenerator( 5 );
12 // We can see here that the result of the addFive function is 9,
13 // when passed an argument of 4 14 console.log( addFive( 4 ) == 9 );
使用匿名函数来隐藏全局作用域中的变量
2 (function(){
3 // The variable that would normally be global
4 var msg = 'Thanks for visiting! ';
5 // Binding a new function to a global object
6 window.onload = function(){
7 // Which uses the 'hidden' variable
8 console.log( msg );
9 };
10 // Close off the anonymous function and execute it 11 })();
闭包允许引用存在于父函数中的变量。但是它在刚创建的时候并不提供变量的值。它提供的是变量最终的值。
var obj = document.getElementById('main');
// An array of items to bind to
var items = ['click', 'keypress' ];
// Iterate through each of the items
for ( var i = 0; i < items.length; i++ ) {
// Use a self-executed anonymous function to induce scope
(function(){
// Remember the value within this scope
// Each 'item' is unique.
//Not relying on variables created in the parent context.
var item = items[i];
// Bind a function to the element
obj['on' + item ] = function() {
// item refers to a parent variable that has been successfully
// scoped within the context of this for loop
console.log('Thanks for your ' + item );
};
})();
}
函数重载类型检查
JavaScript并不提供函数重载的特性,但是存在功能让我们可以实现重载。
函数的重载我们需要知道两件事参数的数量和参数的类型:
我们可以借助每个JavaScript函数都有的语境变量arguments类数组对象。
重载的例子:
function sendMessage( msg, obj ) {
// If both a message and an object are provided
if ( arguments.length === 2 ) {
// Send the message to the object
// (Assumes that obj has a log property!)
obj.log( msg );
} else {
// Otherwise, assume that only a message was provided
// So just display the default error message
console.log( msg );
}
}
// Both of these function calls work
sendMessage( 'Hello, World!' );sendMessage( 'How are you?', console );
你可能想知道有没有办法让arguments拥有Array的全部功能。就其本身来说是不可能的。但是arguments的拷贝倒是可以
将arguments转换成数组
var argsArray = Array.prototype.slice.call( arguments, 0 );
console.log( 'The last argument is: ' + argsArray.pop() );
}
// Will output 'The last argument is 3'. aFunction( 1, 2, 3 );
关于类型检查
第一种就是使用typeof检查
var arr = 'apples,oranges,pears';
// Check to see if our number is actually a string
if ( typeof num === 'string' ) {
// If it is, then parse a number out of it
num = parseInt( num );
}
// Check to see if our array is actually a string
if ( typeof arr == 'string' ) {
// If that's the case, make an array, splitting on commas
arr = arr.split( ',' );
}
在变量不是Object Array 或者是 自定义对象的时候,这个方案是完美的。
第二种方式是使用instanceof,它检查右操作数是不是左操作数的构造函数
var re = /[a-z]+/i;
// These don't give us enough details
console.log('typeof today: ' + typeof today);
console.log('typeof re: ' + typeof re);
// Let's find out if the variables are of a more specific type
if (today instanceof Date) {
console.log('today is an instance of a Date.');
}
更好的检查方法是使用Object.prototype.toString.call(obj).
新的对象工具
对象的修改JavaScript有3种方法控制一个对象是否可以被修改。
Object.preventExtensions()阻止添加新的属性,强行添加会导致typeError.但是可以修改\删除已经存在的属性
var obj = {};
// Creates a new Object object using preventExtensions
var obj2 = Object.preventExtensions(obj);
// Generates TypeError when trying to define a new property
function makeTypeError(){
'use strict';
//Generates TypeError when trying to define a new property
Object.defineProperty(obj2, 'greeting',{value: 'Hello World'});
} makeTypeError();
第二种Object.seal()不能新添属性和删除属性及转换成getter。
var obj = {};
obj.greeting = 'Welcome';
Object.seal(obj);
//Updating the existing writable property
//Cannot convert existing property to accessor, throws TypeErrors
obj.greeting = 'Hello World';
Object.defineProperty(obj, 'greeting', {get:function(){return 'Hello World'; } });
// Cannot delete property, fails silently
delete obj.greeting;
function makeTypeError(){
'use strict';
//Generates TypeError when trying to delete a property
delete obj.greeting;
//Can still update property
obj.greeting = 'Welcome';
console.log(obj.greeting);
}
makeTypeError();
第三种Object.freeze
这里的对象就是不能有任何的改变了,但是它的某个属性是对象的话仍然可以改变的
var obj = {
greeting: "Welcome",
innerObj: {}
};
//Freeezes our obj
Object.freeze(obj);
//silently fails
obj.greeting = 'Hello World';
//innerObj can still be updated
obj.innerObj.greeting = 'Hello World';
console.log('obj.innerObj.greeting = ' + obj.innerObj.greeting);
//Cannot convert existing property to accessor
//Throws TypeError
Object.defineProperty(obj, 'greeting', {get:function(){return 'Hello World'; } });
// Cannot delete property, fails silently
delete obj.greeting;
function makeTypeError(){
'use strict';
}
//Generates TypeError when trying to delete a property
delete obj.greeting;
//Freeze inner object
Object.freeze(obj.innerObj);
//innerObj is now frozen. Fails silently
obj.innerObj.greeting = 'Worked so far...';
function makeTypeError(){
'use strict';
//all attempts will throw TypeErrors
delete obj.greeting;
obj.innerObj.greeting = 'Worked so far...';
obj.greeting = "Welcome";
}; makeTypeError();
浙公网安备 33010602011771号