移動互聯網的迅速崛起,讓移動網頁,移動客戶端越來越重要,客戶端的頁面設計也是一門很大的學問??萍佳杆侔l展的今手機屏幕的尺寸越來越放大化,但卻始終 很有限,因此,在APP的界面設計中,精簡是一貫的準則。這里所說的精簡并不是內容上盡可能的少量,而是要注重重點的表達。在視覺上也要遵循用戶的視覺邏 輯,用戶看著順眼了,才會真正的喜歡。
接下來為大家分享精美的app UI設計案例:
--手機appUI設計--
更多精彩文章:
前言
跨域問題來源于JavaScript的同源策略,即只有 協議+主機名+端口號(如存在)相同,則允許相互訪問。也就是說JavaScript只能訪問和操作自己域下的資源,不能訪問和操作其他域下的資源,主要是安全問題。
在很多時候跨域問題我都是讓后端解決,嘿嘿。但也有需要自己解決的項目!
首先在項目的根目錄下建一個vue.config.js
如下:
//改變webpack的設置
const { default: Axios } = require("axios")
module.exports = {
publicPath :"./",
devServer: {
// 設置主機地址
// host: 'xxx.1xx.1xx.xxx',
// // 設置默認端口
// port: 8051,
// 設置代理
proxy: {
'/api': {
// 目標 API 地址
target: 'http://xxx.xxx.xxx.xxx:8051',//服務器地址
// 如果要代理 websockets
ws: true,
// 將主機標頭的原點更改為目標URL
changeOrigin: false
}
}
},
chainWebpack: config => {//沒有用到scss的這里就不需要啦!
const oneOfsMap = config.module.rule('scss').oneOfs.store
oneOfsMap.forEach(item => {
item
.use('sass-resources-loader')
.loader('sass-resources-loader')
.options({
// 要公用的scss的路徑
resources: './src/assets/base.scss'
})
.end()
})
}
}
轉自:csdn 論壇 作者:可 樂 伢
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
因為學習的時候用的版本比較新,而網上的教程又全是老版本,所以出現了很多問題,總結以下,幫同樣初學的師傅們踩坑了。
廢話不多說:
1:
file->new->project新建一個普通java項目:
工程名可以隨意命名
2:
工程名上右鍵->Add Framework Support:
在Web Application上打勾,點擊OK
3:
展開工程名->web->WEB-INF,在WEB-INF下新建兩個文件夾,分別是classes、lib:
4:
按下ctrl+alt+shift+S,調出Project Structure,
選到Modules->Paths,單選框選到use module xxxxx,將兩個路徑改為剛才創建的classes。
然后選到Dependencies,點擊下面的+號,選擇jars or dirxxxxxxxx,選擇剛創建的lib目錄,讓選擇目錄用處的話,選擇jar direxxxxxxx,打上勾,點擊apply,OK
5:
將tomcat/lib目錄下的servlet-api.jar復制到我們創建的lib目錄里。
6:
點擊右上角小錘子旁邊的Add Configuration,點擊加號,選擇tomcat server->local。這里注意不要選成tomEE的,兩者圖標一樣,但是不是一個東西。其他配置不變,點擊aplly上面的fix,application context可以隨意命名,建議一個/就可以。然后aplly,OK。
7:
改一改index.jsp中帶的title和end,運行一下,如果類似以下,那基本就OK了。
8:
在src里面新建一個java class,嘗試寫一個servlet:
這里也是與其他版本不同的地方,老版本都是import javax.servlet.xxxxx,這里是import jakarta.servlet.xxxxx,具體應該import的包,可以展開servlet-api.jar看到。
import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; @WebServlet(name = "login") public class Login extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML>"); out.println("<HTML>"); out.println(" <HEAD><TITLE>login</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" this is login page"); out.print(this.getClass()); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML>"); out.println("<HTML>"); out.println(" <HEAD><TITLE>login</TITLE></HEAD>"); out.println(" <BODY>"); out.print(" this is login page"); out.print(this.getClass()); out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close(); } }
然后修改web.xml文件,如下:
servlet-name可以任意命名,只要上下兩個一致就可以,servlet-class應該與類名相同,url-pattern是與java class中的@WebServlet(name=“xxxx”)的xxxx相同,這里的xxxx就是路徑。
此時編譯并運行,在地址欄輸入我們寫的url,就可以訪問到動態資源了:
全篇結束,只是記錄踩坑,希望能對大家有幫助。
轉自:csdn 論壇 作者:Hausa_
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
function _getValue(target, valuePath, defalutVal) {
let valueType = Object.prototype.toString.call(target)
console.log(valueType)
// if (valueType == "[object Array]") {
let paths = valuePath.replace(/\[(\d+)\]/, `.$1`).split('.')
let result = target
for(const path of paths){
result = Object(result)[path]
if(result == undefined){
return defalutVal
}
}
return result
}
測試:
let obj = {
a:{
b:[
{
c:2
}
]
}
}
console.log(_getValue(obj, 'a.b[0].c')) //2
function isEqual(res1, res2) {
let a = getTypeOf(res1)
let b = getTypeOf(res2)
if(a !== b){
return false
}else if(a === 'base'){
console.log('base',res1,res2)
return res1 === res2
} else if(a === 'array'){
if(res1.length !== res2.length){
console.log('array',res1,res2)
return false
}else{
//遍歷數組的值比較
for(let i =0;i<res1.length;i++){
if(!isEqual(res1[i],res2[i])){
console.log('array',res1[i],res2[i])
return false
}
}
return true
}
return true
}else if(a === 'object'){
let ak = Object.keys(a)
let bk = Object.keys(b)
if(ak.length !== bk.length){
return false
}else{
for(let o in res1){
console.log(res1[o])
if(!isEqual(res1[o],res2[o])){
console.log('object',res1[o],res2[o])
return false
}
}
return true
}
}else if(a === 'null' || a === 'undefined'){
console.log('null')
return true
}else if(a === 'function'){
console.log('function')
return a === b
}
}
function getTypeOf(res) {
let type = Object.prototype.toString.call(res)
switch (type) {
case "[object Array]":
return 'array'
case "[object Object]":
return 'object'
case "[object Null]":
return 'null'
case "[object Undefined]":
return 'undefined'
case "[object Number]"||"[object String]"||"[object Boolean]":
return 'base'
case "[object Function]":
return 'function'
default:
return 'typeError'
}
}
測試:
let a = {
a:20,
b:{
c:30,
d:[1,2,3]
}
}
let b = {
a:20,
b:{
c:30,
d:[1,2,3]
}
}
console.log(isEqual(a,b)) //true
function _flat(arr){
let result = []
for(let i = 0;i<arr.length;i++){
if(Array.isArray(arr[i])){
result = result.concat(_flat(arr[i]))
}else{
result.push(arr[i])
}
}
return result;
}
let arr = [1,2,[3,4,[5,6]]]
_flat(arr) //[1,2,3,4,5,6]
//es6
function _flat2(arr){
while(arr.some(item=>Array.isArray(item))){
arr = [].concat(...arr)
}
return arr
}
let arr = [1,2,[3,4,[5,6]]]
_flat2(arr) //[1,2,3,4,5,6]
簡單深克隆,不考慮內置對象和函數
function deepClone(obj){
if(typeof obj !== 'object') return
let newObj = obj instanceof Array?[]:{}
for(let key in obj){
if(obj.hasOwnProperty(key)){
newObj[key] = typeof obj[key] === 'object'?deepClone(obj[key]):obj[key]
}
}
return newObj
}
復雜版深度克隆 考慮內置對象 比如date regexp 函數 以及對象的循環引用的問題
const isObject = (target) => typeof target === "object"&& target !== null;
function deepClone2(target, map = new WeakMap()) {
console.log(target)
if (map.get(target)) {
return target;
}
// 獲取當前值的構造函數:獲取它的類型
let constructor = target.constructor;
// 檢測當前對象target是否與正則、日期格式對象匹配
if (/^(RegExp|Date)$/i.test(constructor.name)) {
// 創建一個新的特殊對象(正則類/日期類)的實例
return new constructor(target);
}
if (isObject(target)) {
map.set(target, true); // 為循環引用的對象做標記
const cloneTarget = Array.isArray(target) ? [] : {};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], map);
}
}
return cloneTarget;
} else {
return target;
}
}
filter去重
function _unique(arr){
return arr.filter((item,index,array)=>{
return array.indexOf(item) === index
})
}
es6 Set
function _unique2(arr){
return [...new Set(arr)]
}
includes
function _unique3(arr){
let newArr = []
arr.forEach(item => {
if(!newArr.includes(item)){
newArr.push(item)
}
});
return newArr
}
雙層for循環
function _unique4(arr){
for(let i =0;i<arr.length;i++){
for(let j =i+1;j<arr.length;j++){
if(arr[i] === arr[j]){
arr.splice(j,1)
j--
}
}
}
return arr
}
indexof
function _unique5(arr){
let newArr = []
for(let i = 0;i<arr.length;i++){
if(newArr.indexOf(arr[i] === -1){
newArr.push(arr[i])
})
}
return newArr
}
function _typeOf(obj){
let res = Object.prototype.toString.call(obj).split(' ')[1]
let mold = res.substring(0,res.length-1).toLowerCase()
return mold
}
_typeOf(5) //number
_typeOf('5') //string
function getParamsObj(params){
let paramsStr = params.replace(/^.+\?(.+)/,"$1")
let paramsArr = paramsStr.split('&')
let paramsObj = {}
for(let [key,value] of paramsArr.entries()){
if(/=/.test(value)){
let valArr = value.split('=')
val = decodeURIComponent(valArr[1]) //解碼
val = /^\d+$/.test(val)?parseFloat(val):val //判斷是不是數字
if(paramsObj.hasOwnProperty(valArr[0])){
paramsObj[valArr[0]] = [].concat(paramsObj[valArr[0]],val)
}else{
paramsObj[valArr[0]] = val
}
}
}
return paramsObj
}
//從一次傳入多個參數 編程多次調用每次傳入一個參數
function add(a, b, c, d, e) {
return a + b + c + d + e
}
function curry(fn) {
let dFn = (...args)=>{
if(args.length == fn.length) return fn(...args)
return (...arg)=>{
return dFn(...args,...arg)
}
}
return dFn
}
let addCurry = curry(add)
addCurry(1,2,3)(2)(3)
//添加了兩個功能
// 圖片加載完成后 移除事件監聽
// 加載完的圖片從imgList中移除
let imgList = [...document.querySelectorAll('img')]
let length = imgList.length
const imgLazyLoad = function () {
let count = 0
let deleteIndexList = []
imgList.forEach((img, index) => {
let rect = img.getBoundingClientRect()
//獲取元素到視圖的距離 top元素上邊到視圖上邊的距離 left元素左邊到視圖左邊的距離 right... bottom...
if (rect.top < window.innerHeight) {
// img.src = img.dataset.src
img.src = img.getAttribute('data-src')
deleteIndexList.push(index)
count++
if (count === length) {
document.removeEventListener('scroll', imgLazyLoad)
}
}
})
imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
}
imgLazyLoad()
document.addEventListener('scroll', imgLazyLoad)
圖片懶加載:https://juejin.cn/post/6844903856489365518#heading-19
函數防抖 觸發高頻事件 事件在n后執行,如果n秒鐘重復執行了 則時間重置
//簡易版
function debounce(func,wait){
let timer;
return function(){
let context = this;
let args = arguments;
console.log(timer)
clearTimeout(timer)
timer = setTimeout(function(){
func.apply(context,args)
},wait)
}
}
let btn = document.querySelector('button');
function aa(){
console.log(111)
}
btn.onclick = debounce(aa,2000)
// 復雜版
// 1.取消防抖
// 2.立即執行功能(點擊之后立即執行函數 但是 wait時間之后在點擊才能在立即執行)
// 3.函數可能有返回值
function debounce(func,wait,immediate){
let timer,result;
const debounce = function () {
const context = this
const args = arguments
if(timer) clearTimeout(timer)
if(immediate){
console.log(timer)
var callNow = !timer
timer = setTimeout(function () {
timer =null
},wait)
if(callNow) result = func.apply(context,args)
}else{
timer = setTimeout(function (params) {
result = func.apply(context,args)
},wait)
}
return result
}
debounce.cance = function () {
clearTimeout(timer)
timer=null
}
return debounce
}
let btn = document.querySelector('button');
function aa(){
console.log(111)
}
btn.onclick = debounce(aa,2000,true)```
函數節流 觸發高頻事件 且n秒只執行一次
//使用時間戳
function throttle(func,wait) {
var context,args;
var previous = 0
return function () {
context = this;
args = arguments;
let nowDate = +new Date()
if(nowDate-previous>wait){
func.apply(context,arguments)
previous = nowDate
}
}
}
//定時器
function throttle(func,wait) {
var context,args;
var timer;
return function(){
context = this;
args = arguments;
if(!timer){
timer = setTimeout(function () {
timer = null;
func.apply(context,args)
},wait)
}
}
}
//組合版 options.leading 為true 立即執行一次 options.trailing為true 結束之后執行一次 默認為true function throttle(func, wait ,options = {}) { var context, args, timer,result; var previous = 0; var later = function () { previous = options.leading === false ? 0 : new Date().getTime(); timer = null; func.apply(context, args) if (!timer) context = args = null; } var throttle = function () { var now = new Date().getTime() if (!previous && options.leading === false) previous = now; context = this; args = arguments; //下次觸發 func 剩余的時間 var remaining = wait - (now - previous); if (remaining <= 0 || remaining > wait) { // if (timer) { // clearTimeout(timer); // timer = null; // } previous = now; func.apply(context, args); if (!timer) context = args = null; } else if (!timer&& options.trailing !== false) { timer = setTimeout(later, remaining); } } throttled.cancel = function() { clearTimeout(timer); previous = 0; timer = null; } return throttle } function aa(e) { console.log(111) console.log(e) } let btn = document.querySelector('button'); btn.onclick = throttle(aa, 2000,{ leading:false, trailing:true
})
轉自:csdn論壇 作者:Selfimpr歐
移動互聯網的迅速崛起,讓移動網頁,移動客戶端越來越重要,客戶端的頁面設計也是一門很大的學問??萍佳杆侔l展的今手機屏幕的尺寸越來越放大化,但卻始終 很有限,因此,在APP的界面設計中,精簡是一貫的準則。這里所說的精簡并不是內容上盡可能的少量,而是要注重重點的表達。在視覺上也要遵循用戶的視覺邏 輯,用戶看著順眼了,才會真正的喜歡。
接下來為大家分享精美的app UI設計案例:
icon的設計會貫穿全套設計稿,所以在設計的環節中必不可少,優質的icon設計會幫助品牌和企業更好的樹立形象,形成自己的設計語言。
接下來為大家分享一些經典案例:
--手機appUI設計--
--icon圖標賞析--
更多精彩文章:
1.Object.defineProperty(obj,prop,descriptor)這個語法內有三個參數,分別是obj(要定義其上屬性的對象) prop (要定義或修改的屬性)descriptor (具體的改變方法)
2.簡單的說 就是用這個方法來定義一個值。當調用時我們使用了它里面的get方法,當我們給這個屬性賦值的時候,又用到了它里面的set方法
var obj = {}; Object.defineProperty(obj,'hello',{ get: function(){ console.log('調用了get方法') }, set: function(newValue){ console.log('調用了set方法,方法的值是' + newValue); } }); obj.hello; // => '調用了get方法' obj.hello = 'hi'; // => '調用了set方法,方法的值是hi'
原文來自于這里,我說一下我自己的理解,其實發布-訂閱模式和觀察者模式很像,但是不同的是,觀察者模式只有兩個角色,而且Obsever是知道Subject的,但是在發布-訂閱模式中,他們兩卻彼此不了解,他們是在一種類似于中間件的幫助下進行通信的,換句話說,還有第三個組件,稱為代理或消息代理或事件總線,Observer和Subject都知道該組件,該組件過濾所有傳入消息并相應的分發他們。
<input type="text"> <p></p>
我們要對上面兩個DOM元素實現雙向數據綁定,就是當輸入inputValue時下面的p可以及時更新inputValue內容
<script> let input = document.querySelector('input') let p = document.querySelector('p') let obj = {} let value = '' Object.defineProperty(obj, 'inputvalue', { get() { return value }, set(newValue) { input.value = newValue
p.innerHTML = newValue } }) // 訂閱者 DOM元素 input.value = obj.inputvalue
p.innerHTML = obj.inputvalue // 監聽輸入的事件 input.addEventListener('keyup', function (e) { // 修改inputvalue 達到修改input.value 以及input.innerHTML // 發布者 obj.inputvalue = e.target.value // 觸發了set }) </script>
所以在我們的代碼中,訂閱者就是頁面中的DOM元素,因為他會訂閱我們的inputvalue,而發布者就是監聽事件中的數據,一旦監聽到了數據有修改,就要發布給我們的訂閱者,也就是說輸入的數據一旦發生了變化,我們的頁面DOM元素的數據也會發生變化,所以這個中間件就是Object.defineProperty中的set方法
轉自:csdn 論壇 作者:Y shǔ shǔ
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
一、程序設計語言中的兩大編程思想:面向對象、面向過程
(一)、面向過程
(二)、面向對象
(三)、什么是對象?
自定義對象的方式主要有以下幾種:
字面量形式、工廠形式、構造方法
(四)、字面量形式的創建
格式:
var 對象名稱={ 屬性名稱1:屬性,1,
屬性名稱2:屬性值2,
屬性名稱3:屬性值3,
屬性名稱n:屬性值n, };
沙場練兵:
<!-- 創建一個汽車對象 1、屬性:品牌、價格、顏色等、 2、方法(功能):跑、停 --> <body> <script> var car = { brand: '寶馬', price: '100萬', color: 'red', run: function() { console.log('汽車跑起來了'); }, stop: function() { console.log('汽車停下來了'); } }; console.log(car); </script>
<script> var person = { name: '小王', age: '18', gender: '女', eat: function() { console.log('方便面'); }, play: function() { console.log('王者榮耀'); }, study: function() { console.log('web前端'); } }; console.log(person); </script>
轉自:csdn論壇 作者:乘風破浪的程序媛
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
事情是這樣的:大家都知道“內存泄露”這回事吧。它有幾個常見的場景:
內存泄漏需要重視,它是如此嚴重甚至會導致頁面卡頓,影響用戶體驗!
其中第 3 點引起了我的注意 —— 我當然清楚地知道它說的是比如:“假設你手動移除了某個dom節點,本應釋放該dom節點所占用的內存,但卻因為疏忽導致某處代碼仍對該被移除節點有引用,最終導致該節點所占內存無法被釋放”的情況
<div id="root"> <div class="child">我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') let child = document.querySelector('.child') let root = document.querySelector('#root') btn.addEventListener('click', function() { root.removeChild(child) }) </script>
該代碼所做的操作就是點擊按鈕后移除.child
的節點,雖然點擊后,該節點確實從dom被移除了,但全局變量child仍對該節點有引用,所以導致該節點的內存一直無法被釋放。
解決辦法:我們可以將對.child
節點的引用移動到click事件的回調函數中,那么當移除節點并退出回調函數的執行上文后就會自動清除對該節點的引用,自然也就不會存在內存泄漏的情況了。(這實際上是在事件中實時檢測該節點是否存在,如果不存在則瀏覽器必不會觸發remove函數的執行)
<div id="root"> <div class="child">我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') btn.addEventListener('click', function() { let child = document.querySelector('.child') let root = document.querySelector('#root') root.removeChild(child) }) </script>
這段代碼很完美么?不。因為它在每次事件觸發后都創建了對child和root節點的引用。消耗了內存(你完全可以想象一些人會狂點按鈕的情況…)。
其實還有一種辦法:我們在click中去判斷當前root節點中是否還存在child子節點,如果存在,則執行remove函數,否則什么也不做!
這就引發了標題中所說的行為。
怎么判斷?
遍歷?不,太過麻煩!
不知怎的,我突然想到了 for...in
中的 in 操作符,它可以基于原型鏈遍歷對象!
我們來還原一下當時的場景:打開GitHub,隨便找一個父節點,并獲取它:
圖中畫紅框的就是我們要取的父元素,橘紅色框的就是要判斷是否存在的子元素。
let parent=document.querySelector('.position-relative'); let child=document.querySelector('.progress-pjax-loader');
這里注意,因為獲取到的是DOM節點(類數組對象),所以我們在操作前一定要先處理一下:
let p_child=[...parent.children];
然后
console.log(child in p_child);
?。?!
為什么呢?(此時筆者還沒有意識到事情的嚴重性)
我想,是不是哪里出了問題,用es6的includes
API驗證一下:
console.log(p_child.includes(child));
沒錯?。?br style="box-sizing:border-box;outline:0px;user-select:text !important;overflow-wrap:break-word;" />
再用一般的數組驗證一下:
???
此時,筆者才想起到MDN上查閱一番:
進而我發現:in操作符單獨使用時它檢測的是左側的值(作為索引)對應的值是否在右側的對象內部(屬性 & 原型上)!
回到上面的代碼中,我們發現:
這驗證了我們的結論。
很顯然,“子元素”并不等同于“存在于原型鏈上” —— 這又引出了一個知識點:attribute和property的區別!
所以經過一番“折騰”,源代碼還是應該直接這樣寫:
<div id="root"> <div class="child">我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') let child = document.querySelector('.child') let root = document.querySelector('#root') let r_child = [...root.children] btn.addEventListener('click', function() { if(r_child.includes(child)){ // 或者你這里直接判斷child是否為null也可以...吧 root.removeChild(child) } }) </script>
轉自:csdn論壇 作者:恪愚
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
本章將專門介紹與執行上下文創建階段直接相關的最后一個細節——this是什么?以及它的指向到底是什么。
也許你在其他面向對象的編程語言曾經看過this
,也知道它會指向某個構造器(constructor)所建立的對象。但事實上在JavaScript里面,this
所代表的不僅僅是那個被建立的對象。
先來看看ECMAScript 標準規范對this 的定義:
「The this keyword evaluates to the value of the ThisBinding of the current execution context.」
「this 這個關鍵字代表的值為當前執行上下文的ThisBinding。」
然后再來看看MDN 對this 的定義:
「In most cases, the value of this is determined by how a function is called.」
「在大多數的情況下,this 其值取決于函數的調用方式?!?
好,如果上面兩行就看得懂的話那么就不用再往下看了,Congratulations!
… 我想應該不會,至少我光看這兩行還是不懂。
先來看個例子吧:
var getGender = function() {
return people1.gender;
};
var people1 = {
gender: 'female',
getGender: getGender
};
var people2 = {
gender: 'male',
getGender: getGender
};
console.log(people1.getGender()); // female
console.log(people2.getGender()); // female
what?怎么people2變性了呢,這不是我想要的結果啊,為什么呢?
因為getGender()
返回(return)寫死了people1.gender
的關系,結果自然是’female’。
那么,如果我們把getGender
稍改一下:
var getGender = function() {
return this.gender;
};
這個時候,你應該會分別得到female
與male
兩種結果。
所以回到前面講的重點,從這個例子可以看出,即便people1
與people2
的getGender
方法參照的都是同一個getGender function,但由于調用的對象不同,所以執行的結果也會不同。
現在我們知道了第一個重點,**this實際上是在函數被調用時發生的綁定,它指向什么完全取決于函數的調用方式。**如何的區分this呢?
看完上面的例子,還是有點似懂非懂吧?那接下來我們來看看不同的調用方式對 this 值的影響。
情況一:全局對象&調用普通函數
在全局環境中,this 指向全局對象,在瀏覽器中,它就是 window 對象。下面的示例中,無論是否是在嚴格模式下,this 都是指向全局對象。
var x = 1
console.log(this.x) // 1
console.log(this.x === x) // true
console.log(this === window) // true
如果普通函數是在全局環境中被調用,在非嚴格模式下,普通函數中 this 也指向全局對象;如果是在嚴格模式下,this 將會是 undefined。ES5 為了使 JavaScript 運行在更有限制性的環境而添加了嚴格模式,嚴格模式為了消除安全隱患,禁止了 this 關鍵字指向全局對象。
var x = 1
function fn() {
console.log(this); // Window 全局對象
console.log(this.x); // 1
}
fn();
使用嚴格模式后:
"use strict" // 使用嚴格模式
var x = 1
function fn() {
console.log(this); // undefined
console.log(this.x); // 報錯 "Cannot read property 'x' of undefined",因為此時 this 是 undefined
}
fn();
情況二:作為對象方法的調用
我們知道,在對象里的值如果是原生值(primitive type;例如,字符串、數值、布爾值),我們會把這個新建立的東西稱為「屬性(property)」;如果對象里面的值是函數(function)的話,我們則會把這個新建立的東西稱為「方法(method)」。
如果函數作為對象的一個方法時,并且作為對象的一個方法被調用時,函數中的this指向這個上一級對象。
var x = 1
var obj = {
x: 2,
fn: function() {
console.log(this);
console.log(this.x);
}
}
obj.fn()
// obj.fn()結果打印出;
// Object {x: 2, fn: function}
// 2
var a = obj.fn
a()
// a()結果打印出:
// Window 全局對象
// 1
在上面的例子中,直接運行 obj.fn() ,調用該函數的上一級對象是 obj,所以 this 指向 obj,得到 this.x 的值是 2;之后我們將 fn 方法首先賦值給變量 a,a 運行在全局環境中,所以此時 this 指向全局對象Window,得到 this.x 為 1。
我們再來看一個例子,如果函數被多個對象嵌套調用,this 會指向什么。
var x = 1
var obj = {
x: 2,
y: {
x: 3,
fn: function() {
console.log(this); // Object {x: 3, fn: function}
console.log(this.x); // 3
}
}
}
obj.y.fn();
為什么結果不是 2 呢,因為在這種情況下記住一句話:this 始終會指向直接調用函數的上一級對象,即 y,上面例子實際執行的是下面的代碼。
var y = {
x: 3,
fn: function() {
console.log(this); // Object {x: 3, fn: function}
console.log(this.x); // 3
}
}
var x = 1
var obj = {
x: 2,
y: y
}
obj.y.fn();
對象可以嵌套,函數也可以,如果函數嵌套,this 會有變化嗎?我們通過下面代碼來探討一下。
var obj = {
y: function() {
console.log(this === obj); // true
console.log(this); // Object {y: function}
fn();
function fn() {
console.log(this === obj); // false
console.log(this); // Window 全局對象
}
}
}
obj.y();
在函數 y 中,this 指向了調用它的上一級對象 obj,這是沒有問題的。但是在嵌套函數 fn 中,this 并不指向 obj。嵌套的函數不會從調用它的函數中繼承 this,當嵌套函數作為函數調用時,其 this 值在非嚴格模式下指向全局對象,在嚴格模式是 undefined,所以上面例子實際執行的是下面的代碼。
function fn() {
console.log(this === obj); // false
console.log(this); // Window 全局對象
}
var obj = {
y: function() {
console.log(this === obj); // true
console.log(this); // Object {y: function}
fn();
}
}
obj.y();
情況三:作為構造函數調用
我們可以使用 new 關鍵字,通過構造函數生成一個實例對象。此時,this 便指向這個新對象。
var x = 1;
function Fn() {
this.x = 2;
console.log(this); // Fn {x: 2}
}
var obj = new Fn(); // obj和Fn(..)調用中的this進行綁定
console.log(obj.x) // 2
使用new
來調用Fn(..)
時,會構造一個新對象并把它(obj)綁定到Fn(..)
調用中的this。還有值得一提的是,如果構造函數返回了非引用類型(string,number,boolean,null,undefined),this 仍然指向實例化的新對象。
var x = 1
function Fn() {
this.x = 2
return {
x: 3
}
}
var a = new Fn()
console.log(a.x) // 3
因為Fn()返回(return)的是一個對象(引用類型),this 會指向這個return的對象。如果return的是一個非引用類型的值呢?
var x = 1
function Fn() {
this.x = 2
return 3
}
var a = new Fn()
console.log(a.x) // 2
情況四:call 和 apply 方法調用
如果你想改變 this 的指向,可以使用 call 或 apply 方法。它們的第一個參數都是指定函數運行時其中的this
指向。如果第一個參數不傳(參數為空)或者傳 null 、undefined,默認 this 指向全局對象(非嚴格模式)或 undefined(嚴格模式)。
var x = 1;
var obj = {
x: 2
}
function fn() {
console.log(this);
console.log(this.x);
}
fn.call(obj)
// Object {x: 2}
// 2
fn.apply(obj)
// Object {x: 2}
// 2
fn.call()
// Window 全局對象
// 1
fn.apply(null)
// Window 全局對象
// 1
fn.call(undefined)
// Window 全局對象
// 1
使用 call 和 apply 時,如果給 this 傳的不是對象,JavaScript 會使用相關構造函數將其轉化為對象,比如傳 number 類型,會進行new Number()
操作,如傳 string 類型,會進行new String()
操作,如傳 boolean 類型,會進行new Boolean()操作。
function fn() {
console.log(Object.prototype.toString.call(this))
}
fn.call('love') // [object String]
fn.apply(1) // [object Number]
fn.call(true) // [object Boolean]
call 和 apply 的區別在于,call 的第二個及后續參數是一個參數列表,apply 的第二個參數是數組。參數列表和參數數組都將作為函數的參數進行執行。
var x = 1
var obj = {
x: 2
}
function Sum(y, z) {
console.log(this.x + y + z)
}
Sum.call(obj, 3, 4) // 9
Sum.apply(obj, [3, 4]) // 9
情況五:bind 方法調用
調用 f.bind(someObject) 會創建一個與 f 具有相同函數體和作用域的函數,但是在這個新函數中,新函數的 this 會永久的指向 bind 傳入的第一個參數,無論這個函數是如何被調用的。
var x = 1
var obj1 = {
x: 2
};
var obj2 = {
x: 3
};
function fn() {
console.log(this);
console.log(this.x);
};
var a = fn.bind(obj1);
var b = a.bind(obj2);
fn();
// Window 全局對象
// 1
a();
// Object {x: 2}
// 2
b();
// Object {x: 2}
// 2
a.call(obj2);
// Object {x: 2}
// 2
在上面的例子中,雖然我們嘗試給函數 a 重新指定 this 的指向,但是它依舊指向第一次 bind 傳入的對象,即使是使用 call 或 apply 方法也不能改變這一事實,即永久的指向 bind 傳入的第一次參數。
情況六:箭頭函數中this指向
值得一提的是,從ES6 開始新增了箭頭函數,先來看看MDN 上對箭頭函數的說明
An arrow function expression has a shorter syntax than a function expression and does notbind its own
this
,arguments
,super
, ornew.target
. Arrow functions are always anonymous. These function expressions are best suited for non-method functions, and they cannot be used as constructors.
這里已經清楚了說明了,箭頭函數沒有自己的this
綁定。箭頭函數中使用的this
,其實是直接包含它的那個函數或函數表達式中的this
。在前面情況二中函數嵌套函數的例子中,被嵌套的函數不會繼承上層函數的 this,如果使用箭頭函數,會發生什么變化呢?
var obj = {
y: function() {
console.log(this === obj); // true
console.log(this); // Object {y: function}
var fn = () => {
console.log(this === obj); // true
console.log(this); // Object {y: function}
}
fn();
}
}
obj.y()
和普通函數不一樣,箭頭函數中的 this 指向了 obj,這是因為它從上一層的函數中繼承了 this,你可以理解為箭頭函數修正了 this 的指向。所以箭頭函數的this不是調用的時候決定的,而是在定義的時候處在的對象就是它的this。
換句話說,箭頭函數的this看外層的是否有函數,如果有,外層函數的this就是內部箭頭函數的this,如果沒有,則this是window。
var obj = {
y: () => {
console.log(this === obj); // false
console.log(this); // Window 全局對象
var fn = () => {
console.log(this === obj); // false
console.log(this); // Window 全局對象
}
fn();
}
}
obj.y()
上例中,雖然存在兩個箭頭函數,其實this取決于最外層的箭頭函數,由于obj是個對象而非函數,所以this指向為Window全局對象。
同 bind 一樣,箭頭函數也很“頑固”,我們無法通過 call 和 apply 來改變 this 的指向,即傳入的第一個參數被忽略。
var x = 1
var obj = {
x: 2
}
var a = () => {
console.log(this.x)
console.log(this)
}
a.call(obj)
// 1
// Window 全局對象
a.apply(obj)
// 1
// Window 全局對象
上面的文字描述過多可能有點干澀,那么就看以下的這張流程圖吧,我覺得這個圖總結的很好,圖中的流程只針對于單個規則。
本篇文章介紹了 this 指向的幾種情況,不同的運行環境和調用方式都會對 this 產生影響??偟膩碚f,函數 this 的指向取決于當前調用該函數的對象,也就是執行時的對象。在這一節中,你需要掌握:
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
<script type="text/javascript" src="JS/vue.js"></script>
注意:引入Vue.js的 script 標簽,必須放在所有的自定義腳本文件的script 之前,否則在自定義的腳本代碼中應用步到Vue.js。
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script>
注意:為了防止出現外部CDN文件不可用的情況,還是建議用戶將Vue.js下載到本地計算機中。
npm install vue
注意:使用NPM方法安裝Vue.js需要在計算機中安裝node.js。
node;js官網:https://nodejs.org/en/,通過node.js官網下載之后,傻瓜式安裝即可。
利用Vue.js進行前端框架開發的常用工具有如下幾個:WebStorm、IDEA、Vscode
前端框架開發常用的工具下載:
(1)WebStorm官網:https://www.jetbrains.com/webstorm/
(2)IDEA官網:https://www.jetbrains.com/idea/
(3)Vscode官網:https://vscode.en.softonic.com/
轉自:csdn 作者:小白_xm
藍藍設計的小編 http://www.syprn.cn